해당 포스트는 "자바 객체지향 디자인 패턴", "JAVA 언어로 배우는 디자인 패턴 입문" 책의 내용을 요약한 것이다.
※프로토타입(Prototype) 패턴
: 동일한 객체를 여러 번 생성해야 하는 비용을 줄이기 위해 고안된 패턴이다.
object 생성 비용이 높을 경우, 예를 들어 db에서 수 백개의 데이터를 읽어들인 후 객체를 생성해야 한다면 매번 객체를 생성할 때 마다 엄청난 비용이 들것이다. 이럴 때 한번만 db에서 읽어들인 후 객체를 생성하고 프로토타입 패턴을 통해 이 객체를 계속 복제해서 사용한다면 비용을 상당히 줄일 수 있다.
ex)
public class person implements Cloneable{ private String gender; private String age; pirvate String work; public Person(String gender, String age, String work){ this.gender = gender; this.age = age; this.work = work; } public Person clone() throws CloneNotSupportedException{ Person p = (Person)super.clone(); return p; } } public class Client{ public static void main(String args[]{ Person p = new Person("man", "13", "work"); Person p1 = p.clone(); } }
위와 같이 자바에서 제공하는 clone 기본 메서드가 있는 데 해당 함수가 protected로 정의되어 있어서 외부에서 호출이 불가능하다 그래서 Cloneable 인터페이스를 상속 받은 후 외부에서 clone 메서드를 호출할 수 있도록 public 메서드 내부에 clone 메서드를 호출해야 한다. 위 main 함수를 보면 Person 객체 p를 생성한 후 p.clone 메서드를 통해서 생성자 호출 없이 복사하여 p1을 생성했다. 만약 Person 생성자 내부에서 db의 데이터를 여러 개 로딩한 후 필드값을 정의하는 메커니즘으로 되어 있다면 매번 new 생성자로 Person 객체를 만드는 것은 많은 비용이 들 것이다. 이 때 clone 메서드를 통해서 복사를 하게 된다면 매번 db에 접근하지 않고도 새로운 Person 객체를 만들 수 있다. 또한 객체 p에 들어 있는 값과 p1에 들어있는 값이 똑같기 때문에 기존의 객체의 값을 전체가 아닌 조금만 수정하고 싶다면 clone 메서드를 통해서 값을 똑같이 복사한 후 값을 일부분만 수정할 수 있다.
그리고 clone 메서드에서 주의할 점이 기본형 타입의 변수가 아닌 사용자가 정의한 클래스 객체를 포함한 reference type 변수를 clone을 통해 복사할 시 값에 의한 복사가 아닌 주소값 복사가 이루어진다. 만약 Person p 클래스의 변수로 사용자가 정의한 Work 클래스의 필드가 있고 p.clone을 통해서 복사하여 객체를 만들었다고 가정하자. 여기서 p객체 기본형 타입 속성을 변경한다면 p1 객체는 아무런 영향을 받지 않는다. 왜냐하면 기본형 타입 변수는 복사할 때 값 자체가 복사되기 때문이다. 하지만 p 겍체에서 사용자가 정의한 Work 클래스 객체 즉, reference type 변수를 수정하게 된다면 p1 객체의 Work 클래스 객체도 마찬가지로 수정이 된다. 왜냐하면 clone을 통한 복사가 reference type 변수일 경우 주소값 복사가 이루어지기 때문이다.
'자바 > 디자인패턴' 카테고리의 다른 글
플라이웨이트 패턴 (0) | 2017.06.08 |
---|---|
퍼사드 패턴 (0) | 2017.06.08 |
컴퍼지트 패턴 (0) | 2017.06.07 |
데코레이터(Decorator) 패턴 (0) | 2017.06.06 |
옵저버(Observer) 패턴(2) - 직접 구현 (0) | 2017.06.06 |
컴퍼지트 패턴
해당 포스트는 "자바 객체지향 디자인 패턴", "JAVA 언어로 배우는 디자인 패턴 입문" 책의 내용을 요약한 것이다.
※컴퍼지트(Composite) 패턴
: 전체-부분의 관계를 갖는 객체들 사이의 관계를 정의할 때 유용하다. 클라이언트는 전체와 부분을 구분하지 않고 동일한 인터페이스를 사용할 수 있다. 또한 부분 객체의 추가나 삭제 등이 있어도 전체 객체의 클래스 코드를 변경하지 않아도 된다.
ex) 컴퓨터에 추가 장치 지원하기
public class Keyboard { private int price; private int power; public Keyboard(int power, int price){ this.power = power; this.price = price; } public int getPrice(){ return price; } public int getPower(){ return power; } } public class Body { private int price; private int power; public Body(int power, int price){ this.power = power; this.price = price; } public int getPrice(){ return price; } public int getPower(){ return power; } } public class Monitor { private int price; private int power; public Monitor(int power, int price){ this.power = power; this.price = price; } public int getPrice(){ return price; } public int getPower(){ return power; } }
public class Computer{ private Body body; private Keyboard keyboard; private Monitor monitor; public void addBody(Body body){ this.body = body; } public void addKeyboard(Keyboard keyboard){ this.keyboard = keyboard; } public void addMonitor(Monitor monitor){ this.monitor = monitor; } public int getPrice(){ return body.getPrice()+keyboard.getPrice()+monitor.getPrice(); } public int get Power(){ return body.getPower() + keyboard.getPower() + monitor.getPower(); } }
위 예젤르 보면 Computer 클래스는 Keyboard, Body, Monitor 객체를 참조한다. 즉 Computer 객체는 전체, 나머지 부품 객체는 부분이라고 할 수 있다. 또한 이들 과의 관계는 현재 합성관게이다.
- 문제점
만약 Computer 클래스의 부품으로 Speaker 클래스를 와Mouse 클래스를 추가한다면 Speaker와 Mouse 객체를 만든 후에 Computer 클래스에서 따로 add 메서드를 만들어서 속성값으로 참조하도록 수정해야 한다. 또한, Computer 클래스의 getPrice()와 getPower() 메서드를 수정해야한다. 즉, 부품을 추가하면 Computer 클래스를 수정해야하고 이는 OCP를 위반하는 것이다. 이렇게, 전체-부분 관계에서 이러한 OCP를 해결하려면 컴퍼지트 패턴을 사용하면 된다. 아래 코드는 컴퍼지트 패턴을 적용한 예제이다.
public abstract class ComputerDevice{ public abstract int getPrice(); public abstract int getPower(); } public class Keyboard extends ComputerDevice { private int price; private int power; public Keyboard(int power, int price){ this.price = price; this.power = power; } public int getPrice(){ return price; } public int getPower(){ return power; } }
ComputerDevice 추상 클래스를 만들어 각각의 부품 객체에서 ComputerDevice 클래스를 상속받게 한다.
public class Computer extends ComputerDevice { private List<ComputerDevice> components = new ArrayList<ComputerDevice>(); public void addComponent(ComputerDevice component){ components.add(component); } public void removeComponent(ComputerDevice component){ components.remove(component); } public int getPrice(){ int price = 0; for(ComputerDevice component : components) price += component.getPrice(); return price; } public int getPower(){ int power = 0; for(ComputerDevice component:components) power += component.getPower(); return power; } }
Computer 클래스도 마찬가지로 ComputerDevice 추상클래스를 상속받게 했고 부품 객체들은 List 콜렉션에서 관리된다.
public class Client{ public static void main(String[] args){ Body body = new Body(100,40); Keyboard keyboard = new Keyboard(50,90); Monitor monitor = new Monitor(40,20); Computer computer = new Computer(); computer.addComponent(body); computer.addComponent(keyboard); computer.addComponent(monitor); System.out.println(computer.getPrice()+"-"computer.getPower()); } }
메인 위와 같다. 만약 여기서 Speaker와 Mouse 부품이 추가 될시 단지 ComputerDevice 클래스를 상속받은 클래스만 추가하고 메인만 추가해주면 된다. Computer 클래스는 수정 없이 부품들의 추가가 가능해 Computer 클래스는 OCP를 준수할 수 있게 된다.
'자바 > 디자인패턴' 카테고리의 다른 글
퍼사드 패턴 (0) | 2017.06.08 |
---|---|
프로토타입 패턴 (0) | 2017.06.08 |
데코레이터(Decorator) 패턴 (0) | 2017.06.06 |
옵저버(Observer) 패턴(2) - 직접 구현 (0) | 2017.06.06 |
옵저버(Observer) 패턴(1) - JDK API 활용 (0) | 2017.06.06 |