반응형
Composite Pattern
- Composite Pattern (복합체 패턴) 이란?
- 전체-부분 관계의 트리 구조로 표현되는 객체들을 단일 객체처럼 취급할 수 있게 해주는 구조 패턴
- 단일 객체와 복합 객체를 동일한 인터페이스를 사용하여 처리하기 위함
- 단일 객체는 Leaf 라고 하고, 단일 객체들의 그룹인 복합 객체는 Composite 이라고 함
- 단일 객체와 복합 객체를 동일한 인터페이스를 사용하여 처리하기 위함
- 전체-부분 관계의 트리 구조로 표현되는 객체들을 단일 객체처럼 취급할 수 있게 해주는 구조 패턴
- 복합체 패턴이 필요한 상황 예시
- 주문 시스템에서 여러 상품을 담은 상자의 가격을 계산하려고 할 때
- 한 box 에 여러 products 와 좀 더 작은 box 들을 담을 수 있음
- 작은 box 들 역시 여러 products 를 담을 수 있다고 있음
- 상자의 가격을 계산하기 위해서는 내부 제품을 모두 살펴보면서 가격을 합산해야 함
- 트리 전체 순회를 하면서 box 와 product 의 type 을 구분해주는 코드를 구성해야 해서 코드가 복잡해질 수 있음
- 주문 시스템에서 여러 상품을 담은 상자의 가격을 계산하려고 할 때
- 복합체 패턴의 아이디어
- 총가격을 계산하는 메소드를 가지는 공통 인터페이스를 통해서 products 와 boxes 에 대해 동일하게 작업하게 할 수 있음
- Product의 경우 단순히 제품의 가격을 반환
- Box 의 경우 Box 내 products 의 총 가격을 반환
- 트리를 구성하는 객체들의 구체적인 클래스 타입에 대해서 신경 쓸 필요가 없음 (현재 보고 있는 객체가 product 인지 box 인지 알 필요가 없음)
- 복합체 패턴은 그릇과 내용물을 동일 시 해서 재귀적인 구조를 편하게 다룰 수 있게 해줌
- 총가격을 계산하는 메소드를 가지는 공통 인터페이스를 통해서 products 와 boxes 에 대해 동일하게 작업하게 할 수 있음
- 복합체 패턴의 구조
- 복합체 패턴의 구조 예시 - Box & Product
- 복합체 패턴 코드 예시
interface Component{ int getPrice(); String getName(); } public class Product implements Component{ String name; int price; public Product(String name, int price){ this.name = name; this.price = price; } @Override public int getPrice(){ return this.price } @Override public String getName(){ return this.name; } } public class Box implements Component{ List<Component> components; String name; public Box(String name){ components = new ArrayList<>(); this.name = name; } public void add(Component item){ components.add(item); } public List<Component> getComponents(){ return components; } @Override public int getPrice(){ int totalprice = 0; for(Component component : components) totalPrice += component.getPrice(); return totalPrice; } @Override public String getName(){ return this.name; } } public class Client{ public void printPrint(Component box){ int totalPrice = box.getPrice(); String line = String.format("Total price of %s: %d", box.getName(), box.getPrice()); System.out.println(line); } } public class Main{ public static void main(String[] args){ Box box = new Box("Main box"); Product armor = new Product("Armor", 250); Product sword = new Product("Sword", 500); box.add(armor); box.add(sword); Box sub_box = new Box("Sub box"); Product apple = new Product("Apple", 400); Product banana = new Product("banana", 130); sub_box.add(apple); sub_box.add(banana); box.add(sub_box); Client client = new Client(); client.printPrint(box); } }
- Java 에서 복합체 패턴 사용 사례
- Java Swing
JFrame frame = new JFrame(); JTextField textField = new JTextField(); textField.setBounds(200,200,200,40); frame.add(textField); JButton button = new JButton("click"); button.setBounds(200,100,60,40); button.addActionListener(e->textField.setText("Hello Swing")); frame.add(button); frame.setSize(600,400); frame.setLayout(null); frame.setVisible(true);
- Java Swing
- 사용 시기
- 객체의 구조가 트리로 표현되는 상황에서, 단일/복합 객체의 관계를 단순화하여 균일하게 처리하고 싶을 때
- 장점
- 다형성 재귀를 통해 복잡한 트리 구조를 보다 편리하게 다룰 수 있음
- OCP 준수
- 새로운 leaf 클래스 추가하더라도 클라이언트에 영향 없음
- 단점
- 재귀 호출 특징 상 트리 깊이가 깊어지면 디버깅에 어려움이 생김
- 기능이 너무 다른 클래스들 간에는 공통 인터페이스 설계가 까다로움
- Component 에 선언되는 메소드가 공통으로 활용될 수 있는 의미를 가져야 함
Decorator Pattern
- 데코레이터 패턴이란?
- 객체에 추가적인 기능을 동적으로 더해줄 수 있게 해주는 구조 패턴
- 컴파일 타임이 아닌 런타임에 객체에 대한 기능 확장이 가능
- 데코레이터(장식자) 라는 뜻은 기본 제품에 재료/디자인을 추가 및 변경해줌으로써 새로운 종류의 제품을 만들어내는 것을 의미
- 객체에 추가적인 기능을 동적으로 더해줄 수 있게 해주는 구조 패턴
- 데코레이터 패턴이 필요한 상황
- 사용자에게 이벤트 알람을 주기 위한 알람 라이브러리를 설계
- Application 에서는 Notifier 를 통해 기본적으로 이메일로 알람을 함
- 어느 시점부터 사용자들이 이메일 이상의 알람 기능을 원하여 아래와 같이 상속 구조로 Notifier 를 확장했다고 가정
- 상속 구조에서는 한번에 특정 Notifier 만 동작하게 됨
- 여러 채널을 통해서 알람을 보내고 싶으면?
- 상속 구조를 유지한 채로는 모든 조합에 따른 Notifier 를 구현해야 함
- 코드의 양도 늘어나고 확장의 유연성도 떨어짐
- 이미 있는 여러 Notifier 들을 runtime 에 조립할 수 있을까? => 데코레이터 패턴
- 데코레이터 패턴의 구조
- 데코레이터 패턴 구조 예시
- 데코레이터 패턴 코드 예시
- SMSDecorator, SlackDecorator 도 아래와 유사하게 구현
- send 메소드를 각각에 맞게 오버라이딩 해주되, super 의 send 를 호출해주어야 함
interface Notifier { void send(String message); } public class DefaultNotifier implements Notifier { public void send(String message) { System.out.println("Email: " + message); } } public class BaseDecorator implements Notifier { private Notifier notifier; public BaseDecorator(Notifier wrapper) { this.notifier = wrapper; } @Override public void send(String message) { this.notifier.send(message); // 기존 기능 유지 } } public class FacebookDecorator extends BaseDecorator { public FacebookDecorator(Notifier wrapper) { super(wrapper); } @Override public void send(String message) { super.send(message); // 기존 기능 호출 System.out.println("[Facebook] " + message); // 추가 기능 } } public class Main { public static void main(String[] args) { boolean facebookEnabled = false; boolean smsEnabled = true; boolean slackEnabled = false; Notifier notifier = new DefaultNotifier(); // 기본 알림 (이메일) if (facebookEnabled) { notifier = new FacebookDecorator(notifier); } if (smsEnabled) { notifier = new SMSDecorator(notifier); } if (slackEnabled) { notifier = new SlackDecorator(notifier); } Application app = new Application(notifier); app.sendMessage("Hello World"); } }
- send 메소드를 각각에 맞게 오버라이딩 해주되, super 의 send 를 호출해주어야 함
- SMSDecorator, SlackDecorator 도 아래와 유사하게 구현
- Java 에서 데코레이터 패턴 사용 사례
- Stream
- 어댑터이면서 동시에 데코레이터라고 볼 수 있음
- Collections
- checkedList(), synchronizedList(), unmodifiableList() 등
- Stream
- 사용 시기
- 객체 책임과 행동이 동적으로 상황/조건에 따라 다양한 기능이 빈번하게 추가/삭제되는 경우
- 객체를 생성하는 코드에 변경이 없으면서 런타임에 추가 가능
- 객체의 결합을 통해 기능이 생성이 되어야 하는 경우
- 상속을 통해 서브 클래싱으로 객체의 동작을 확장하는 것이 어색하거나 불가능할 때
- 객체 책임과 행동이 동적으로 상황/조건에 따라 다양한 기능이 빈번하게 추가/삭제되는 경우
- 장점
- SRP 준수
- 각 데코레이터 클래스 마다 고유의 책임을 가짐
- OCP 준수
- 클라이언트 코드 수정 없이 기능 확장이 필요하면 데코레이터 클래스를 추가하면 됨
- DIP 준수
- 구현체가 아닌 인터페이스를 바라보기 때문
- 데코레이터를 사용하면 상속을 통해 서브클래스로 확장하는 것보다 훨씬 유연하게 기능을 확장할 수 있음
- 객체를 여러 데코레이터로 래핑하여 여러 동작을 클라이언트가 자유롭게 결합할 수 있음
- SRP 준수
- 단점
- 만일 장식자 일부를 동적으로 제거하고 싶다면, wrapper 스택에서 특정 wrapper 를 제거하는 것이 어려움
- 데코레이터를 조합하는 방식에 코드 복잡도가 올라갈 수 있음
- 어느 장식자를 먼저 데코레이팅 하느냐에 따라 데코레이터 스택 순서가 결정되는데, 만일 순서에 의존하지 않는 방식으로 데코레이터를 구현하기는 어려움
- 어느 데코레이터를 먼저 감싸느냐에 따라 그에 대한 행동이 다르기에 순서에 유의
반응형
'3학년 2학기 학사 > 소프트웨어 분석 및 설계' 카테고리의 다른 글
[소프트웨어 분석 및 설계] L21. Interpreter Pattern, Iterator Pattern (1) | 2024.12.13 |
---|---|
[소프트웨어 분석 및 설계] L19. Facade Pattern, Flyweight Pattern, Proxy Pattern (0) | 2024.12.13 |
[소프트웨어 분석 및 설계] L17. Adapter Pattern, Bridge Pattern (3) | 2024.12.13 |
[소프트웨어 분석 및 설계] L16. Abstract Factory Pattern, Builder Pattern, Prototype Pattern (0) | 2024.12.13 |
[소프트웨어 분석 및 설계] L15. Factory Method (팩토리 메소드) (0) | 2024.12.12 |