CS 스터디 16주차
옵저버 패턴 & 스트레티지 패턴 & 컴포지트 패턴
별로 중요한 패턴들은 아니라 한번에 다루게 되었다.
주요 정리만 하고 넘어 갈 예정이다.
옵저버 패턴
옵저버 패턴은 객체의 상태 변화를 관찰하는 옵저버들(리스너)에게 그 변화를 알리는 디자인 패턴으로
주로 이벤트 기반 시스템에서 사용된다.
주체(Subject)와 옵저버(Observer) 로 구성되어 있고 주체는 상태를 관리하고 옵저버는 주체의 상태 변화를 감지하는 특징이 있다.
이 두가지 구성은 서로 느슨하게 결합되어 있기 때문에 주체는 옵저버의 구체적인 구현을 알 필요가 없다.
이러한 느슨한 결합의 특징은 새로운 옵저버를 쉽게 추가할 수 있도록 확장성이 높은 특징을 가진다.
옵저버 패턴의 사용방법
우선 두개의 클래스와 하나의 인터페이스가 필요하다.
1. 주체 클래스 : 상태를 관리하고 옵저버 등록 및 해제 메서드를 가질 주체 클래스를 만든다.
2. 옵저버 인터페이스 : 상태 변화 알림을 받기위한 메서드를 정의 할 인터페이스를 만든다.
3. 옵저버 클래스 : 옵저버 인터페이스를 상속받아 구체적으로 구현 할 옵저버 클래스를 만들어 주체의 상태 변화에 반응한다.
JAVA에서의 구현
import java.util.ArrayList;
import java.util.List;
// 주체 클래스
class Subject {
private List<Observer> observers = new ArrayList<>();
private int state;
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
notifyAllObservers();
}
public void attach(Observer observer) {
observers.add(observer);
}
public void notifyAllObservers() {
for (Observer observer : observers) {
observer.update();
}
}
}
// 옵저버 인터페이스
abstract class Observer {
protected Subject subject;
public abstract void update();
}
// 구체적인 옵저버 클래스
class ConcreteObserver extends Observer {
public ConcreteObserver(Subject subject) {
this.subject = subject;
this.subject.attach(this);
}
@Override
public void update() {
System.out.println("State changed to: " + subject.getState());
}
}
// 사용 예제
public class ObserverPatternDemo {
public static void main(String[] args) {
Subject subject = new Subject();
new ConcreteObserver(subject);
new ConcreteObserver(subject);
subject.setState(10);
subject.setState(20);
}
}
PYTHON에서의 구현
class Subject:
def __init__(self):
self._observers = []
self._state = None
def attach(self, observer):
if observer not in self._observers:
self._observers.append(observer)
def detach(self, observer):
try:
self._observers.remove(observer)
except ValueError:
pass
def notify(self):
for observer in self._observers:
observer.update(self._state)
@property
def state(self):
return self._state
@state.setter
def state(self, value):
self._state = value
self.notify()
class Observer:
def update(self, state):
pass
class ConcreteObserver(Observer):
def update(self, state):
print(f'State changed to: {state}')
# 사용 예제
subject = Subject()
observer1 = ConcreteObserver()
observer2 = ConcreteObserver()
subject.attach(observer1)
subject.attach(observer2)
subject.state = 10
subject.state = 20
스트레티지 패턴
스트레티지 패턴은 런타임에 알고리즘을 선택할 수 있는 디자인 패턴으로
알고리즘을 각각의 클래스에 캡슐화하고 클라이언트 코드에서 알고리즘을 독립적으로 변경할 수 있게 한다.
각각의 클래스에 알고리즘이 캡슐화되어 있기 때문에 독립적이고,
런타임 시점에 알고리즘 교체가 가능하여 유연성이 높다는 특징이 있다.
또한 새로운 알고리즘을 추가하더라도 기존 코드의 변경이 최소화된다.
스트레티지 패턴의 사용방법
1. 전략 인터페이스: 알고리즘을 정의하는 인터페이스를 생성한다.
2. 구체적인 전략 클래스: 전략 인터페이스를 구현하여 다양한 알고리즘을 정의한다.
3. 컨텍스트 클래스: 전략 객체를 사용하여 작업을 수행한다.
JAVA에서의 구현
// 전략 인터페이스
interface Strategy {
public int doOperation(int num1, int num2);
}
// 구체적인 전략 클래스
class OperationAdd implements Strategy {
@Override
public int doOperation(int num1, int num2) {
return num1 + num2;
}
}
class OperationSubtract implements Strategy {
@Override
public int doOperation(int num1, int num2) {
return num1 - num2;
}
}
class OperationMultiply implements Strategy {
@Override
public int doOperation(int num1, int num2) {
return num1 * num2;
}
}
// 컨텍스트 클래스
class Context {
private Strategy strategy;
public Context(Strategy strategy) {
this.strategy = strategy;
}
public int executeStrategy(int num1, int num2) {
return strategy.doOperation(num1, num2);
}
}
// 사용 예제
public class StrategyPatternDemo {
public static void main(String[] args) {
Context context = new Context(new OperationAdd());
System.out.println("10 + 5 = " + context.executeStrategy(10, 5));
context = new Context(new OperationSubtract());
System.out.println("10 - 5 = " + context.executeStrategy(10, 5));
context = new Context(new OperationMultiply());
System.out.println("10 * 5 = " + context.executeStrategy(10, 5));
}
}
PYTHON에서의 구현
from abc import ABC, abstractmethod
# 전략 인터페이스
class Strategy(ABC):
@abstractmethod
def do_operation(self, num1, num2):
pass
# 구체적인 전략 클래스
class OperationAdd(Strategy):
def do_operation(self, num1, num2):
return num1 + num2
class OperationSubtract(Strategy):
def do_operation(self, num1, num2):
return num1 - num2
class OperationMultiply(Strategy):
def do_operation(self, num1, num2):
return num1 * num2
# 컨텍스트 클래스
class Context:
def __init__(self, strategy):
self._strategy = strategy
def execute_strategy(self, num1, num2):
return self._strategy.do_operation(num1, num2)
# 사용 예제
context = Context(OperationAdd())
print("10 + 5 =", context.execute_strategy(10, 5))
context = Context(OperationSubtract())
print("10 - 5 =", context.execute_strategy(10, 5))
context = Context(OperationMultiply())
print("10 * 5 =", context.execute_strategy(10, 5))
컴포지트 패턴
컴포지트 패턴은 객체들을 트리 구조로 구성하여 부분-전체 계층 구조를 표현하는 패턴으로
클라이언트는 개별 객체와 복합 객체를 동일하게 다룰 수 있다.
복합 객체는 자식으로 다른 복합 객체를 포함할 수 있으며,
클라이언트는 개별/복합 객체를 동일하게 처리할 수 있어 일관된 인터페이스를 갖는다.
컴포지트패턴의 사용방법
1. 컴포넌트 인터페이스: 개별 객체와 복합 객체에 공통된 인터페이스를 정의한다.
2. 리프 클래스: 개별 객체를 나타내는 클래스를 정의한다.
3. 컴포지트 클래스: 자식 객체들을 관리하는 클래스를 정의 한다.
JAVA에서의 구현
import java.util.ArrayList;
import java.util.List;
// 컴포넌트 인터페이스
interface Employee {
public void showEmployeeDetails();
}
// 리프 클래스
class Developer implements Employee {
private String name;
private long empId;
public Developer(String name, long empId) {
this.name = name;
this.empId = empId;
}
@Override
public void showEmployeeDetails() {
System.out.println(empId + " " + name);
}
}
class Manager implements Employee {
private String name;
private long empId;
public Manager(String name, long empId) {
this.name = name;
this.empId = empId;
}
@Override
public void showEmployeeDetails() {
System.out.println(empId + " " + name);
}
}
// 컴포지트 클래스
class CompanyDirectory implements Employee {
private List<Employee> employeeList = new ArrayList<>();
@Override
public void showEmployeeDetails() {
for (Employee emp : employeeList) {
emp.showEmployeeDetails();
}
}
public void addEmployee(Employee emp) {
employeeList.add(emp);
}
public void removeEmployee(Employee emp) {
employeeList.remove(emp);
}
}
// 사용 예제
public class CompositePatternDemo {
public static void main(String[] args) {
Developer dev1 = new Developer("John", 1001);
Developer dev2 = new Developer("Doe", 1002);
Manager mgr1 = new Manager("Anna", 2001);
CompanyDirectory engineeringDirectory = new CompanyDirectory();
engineeringDirectory.addEmployee(dev1);
engineeringDirectory.addEmployee(dev2);
CompanyDirectory companyDirectory = new CompanyDirectory();
companyDirectory.addEmployee(mgr1);
companyDirectory.addEmployee(engineeringDirectory);
companyDirectory.showEmployeeDetails();
}
}
PYTHON에서의 구현
from abc import ABC, abstractmethod
# 컴포넌트 인터페이스
class Employee(ABC):
@abstractmethod
def show_employee_details(self):
pass
# 리프 클래스
class Developer(Employee):
def __init__(self, name, emp_id):
self.name = name
self.emp_id = emp_id
def show_employee_details(self):
print(f"{self.emp_id} {self.name}")
class Manager(Employee):
def __init__(self, name, emp_id):
self.name = name
self.emp_id = emp_id
def show_employee_details(self):
print(f"{self.emp_id} {self.name}")
# 컴포지트 클래스
class CompanyDirectory(Employee):
def __init__(self):
self.employee_list = []
def show_employee_details(self):
for employee in self.employee_list:
employee.show_employee_details()
def add_employee(self, employee):
self.employee_list.append(employee)
def remove_employee(self, employee):
self.employee_list.remove(employee)
# 사용 예제
dev1 = Developer("John", 1001)
dev2 = Developer("Doe", 1002)
mgr1 = Manager("Anna", 2001)
engineering_directory = CompanyDirectory()
engineering_directory.add_employee(dev1)
engineering_directory.add_employee(dev2)
company_directory = CompanyDirectory()
company_directory.add_employee(mgr1)
company_directory.add_employee(engineering_directory)
company_directory.show_employee_details()
'🪄Interview > ✏️Study' 카테고리의 다른 글
[JavaScript Study] 2주차 - 이미지 갤러리(gallery-start) (0) | 2024.08.31 |
---|---|
[JavaScript Study] 1주차 - 구구단 구현하기1 (0) | 2024.08.17 |
[CS STUDY INTERVIEW] 15주차 - 싱글톤 패턴 (0) | 2024.07.06 |
[CS STUDY INTERVIEW] 14주차 - 팩토리 메서드 패턴 (0) | 2024.06.20 |
[CS STUDY INTERVIEW] 14주차 - 템플릿 메서드 패턴 (1) | 2024.06.19 |