✍️ 프로토타입 패턴, Java에서 찾아보기
여기 Student를 담는 ArrayList가 있을 때, students를 복제를 하고 싶다면 ArrayList가 제공하는 clone 메서드를 호출할 수 있다. 하지만 이 방법은 자주 쓰이지 않는다.
public static void main(String[] args) {
Student kang = new Student("kang");
Student world = new Student("world");
ArrayList<Student> students = new ArrayList<>();
students.add(kang);
students.add(world);
ArrayList<Student> clones = (ArrayList<Student>) students.clone();
}
@AllArgsConstructor
public class Student {
private String name;
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
'}';
}
}
ArrayList의 clone을 사용하지 않는 대표적인 이유는 변수의 타입을 지정할 때 가능한 추상적인 타입을 사용하려 하기 때문이다. 추상 타입은 코딩의 유연성과 관련이 있는데 변수를 추상 타입으로 선언하면 얼마든지 구체적인 클래스의 인스턴스로 갈아끼울 수 있기 때문이다. 다시 말해 일반적으로 ArrayList가 아닌 List를 사용하고, List는 clone을 제공하지 않는다.
public static void main(String[] args) {
Student kang = new Student("kang");
Student world = new Student("world");
List<Student> students = new ArrayList<>();
students.add(kang);
students.add(world);
}
프로토타입 패턴과 관련은 없지만 Collection을 복사(얕은 복사)하는 대표적인 방법으로 ArrayList의 생성자를 사용할 수 있다.
List<Student> clone = new ArrayList<>(students);
🧰 ModelMapper
프로토타입 패턴과 관련은 없지만.. 프로토타입 패턴과 유사하게 기존 인스턴스로 새로운 인스턴스를 구성할 때 유용한 라이브러리 ModelMapper를 정리하려 한다.
ModelMapper란, 인스턴스의 필드를 특정 클래스의 인스턴스 필드로 매핑할 때 사용하는 라이브러리다.
말이 조금 어렵다.. 예제를 보자.
여기 사람을 정의한 Person 클래스가 있고, 국가 정보를 정의한 Country 클래스가 있다.
@Data
public class Person {
private String name;
private String address;
private int age;
private Country country;
}
@Data
public class Country {
private String countryName; // 국가명
private String continent; // 대륙
private int phone; // 국가 번호
public void loadKoreaDataFromDB() {
countryName = "Korea";
continent = "Asia";
phone = +82;
}
}
가령 한국인을 표한하고 싶다면 다음과 같이 코드를 작성할 수 있다.
public static void main(String[] args) {
Country korea = new Country();
korea.loadKoreaDataFromDB();
Person korean = new Person("kangworld", "seoul", 20, korea);
}
그리고 여기 Person 클래스의 필드를 플랫하게 정의한 PersonData 클래스가 있다.
@Data
public class PersonData {
private String name; // 사람 이름
private String address; // 주소
private int age; // 나이
private String countryName; // 국가명
private String countryContinent; // 대륙
private int countryPhone; // 국가 번호
}
만약 Person 타입의 인스턴스를 PersonData 타입의 인스턴스로 옮겨 담고 싶다면 get set 메서드를 반복적으로 호출하면 된다. 다만 필드가 많아질수록 코드의 길이도 증가하고 실수가 발생할 수 있다.
public static void main(String[] args) {
Country korea = new Country();
korea.loadKoreaDataFromDB();
Person korean = new Person("kangworld", "seoul", 20, korea);
PersonData koreanData = new PersonData();
koreanData.setName(korean.getName());
koreanData.setAddress(korean.getAddress());
}
바로 앞선 상황에서 ModelMapper 라이브러리를 사용하면 쉽게 인스턴스를 옮겨 담을 수 있다.
먼저 ModelMapper 인스턴스를 만들고 map 메서드에 source(Person)와 destination(PersonData)을 차례대로 넘겨주면 source의 필드값으로 채워진 PersonData 인스턴스를 반환한다.
public static void main(String[] args) {
Country korea = new Country();
korea.loadKoreaDataFromDB();
Person korean = new Person("kangworld", "seoul", 20, korea);
ModelMapper modelMapper = new ModelMapper();
PersonData koreanData = modelMapper.map(korean, PersonData.class);
System.out.println(koreanData);
}
이렇게 필드의 이름과 계층구조를 파악해서 값을 옮겨 담을 수 있는 이유는 Java가 제공하는 reflection 덕분이다. 더 자세한 원리는 reflection을 참고하길 바라고 ModelMapper의 필드 매칭 전략과 같은 디테일은 공식 레퍼런스를 참고하길 바란다.
인프런의 백기선님의 강의 코딩으로 학습하는 GoF의 디자인 패턴을 참고해서 작성했습니다.
'Java > Design Pattern with Java' 카테고리의 다른 글
[구조 패턴] Chapter 6-2. Adapter Pattern : 패턴 적용하기 (0) | 2022.06.13 |
---|---|
[구조 패턴] Chapter 6-1. Adapter Pattern : 패턴 소개 (0) | 2022.06.04 |
[객체 생성 패턴] Chapter 5-3. Prototype Pattern : 장단점 (0) | 2022.05.30 |
[객체 생성 패턴] Chapter 5-2. Prototype Pattern : 패턴 적용하기 (0) | 2022.05.30 |
[객체 생성 패턴] Chapter 5-1. Prototype Pattern : 패턴 소개 (0) | 2022.05.24 |