Tiny Star

🪄Interview/✏️Study

[CS STUDY INTERVIEW] 16주차 - 옵저버 패턴 & 스트레티지 패턴 & 컴포지트 패턴

청크 2024. 7. 16. 22:34

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()