옵저버 패턴에서는 한 객체의 상태가 바뀌면 그 객체에 의존하는 다른 객체들한테 상태변화를 알리고, 내용을 갱신해주는 일대다(one to many)의존성을 정의한다.
class Subject:
def __init__(self):
self.__observers = []
def register_observer(self, observer):
self.__observers.append(observer)
def unregister_observer(self, observer):
if observer in self.__observers
self.__observer.remove(observer)
def notify_observers(self, *args, **kwargs):
for observer in self.__observers:
observer.notify(self, *args, **kwargs)
class Observer:
def __init__(self, subject):
subject.register_observer(self)
def notify(self, subject, *args, **kwargs):
print('Got', args, kwargs, 'From', subject)
subject = Observable()
observer = Observer(subject)
subject.notify_observers('test')
알고리즘군을 정의하고 각각 캡슐화하여 교환해서 사용할 수 있도록 만든다. 스트래티지를 활용하면 알고리즘과 알고리즘을 사용하는 클라언트를 분리하여 관리할 수 있다.
// 행위 인터페이스
public interface FlyBehavior {
public void fly() { }
}
// 행위 구현체
public class FlyWithWings implements FlyBehavior {
public void fly() {
System.out.println("I'm flying!!");
}
}
// 행위를 할 객체의 클래스
public abstract class Duck {
FlyBehavior flyBehavior;
public Duck() { }
public void setFlyBehavior(FlyBehavior fb) {
flyBehavior = fb;
}
// 행위를 표현하는 객체 사용
public void performFly() {
flyBehavior.fly();
}
// 서브클래스에서 공통적으로 정의되어야할 메소드
abstract void display();
// 서브클래스 모두 공통적으로 같은 행위를 하는 메소드
public void swim() {
System.out.println("All ducks float, even decoys!");
}
}
public class WingsDuck implements Duck {
public WingDuck() {}
}
public class Runner() {
public static void main(String[] args) {
Duck wingDuck = new WingDuck();
wingDuck.setFlyBehavior(new FlyWithWings());
}
}
객체에 추가적인 요건을 동적으로 첨가한다. 데코레이터는 서브클래스를 만드는 것을 통해서 기능을 유연하게 확장할 수 있는 방법을 제공한다.
public abstract class Beverage {
public abstract double cost();
}
// 첨가물을 나타내는 추상클래스(데코레이터)
public abstract class CodimentDecorator extends Beverage {}
public class Esopresso extends Beverage {
public double cost() {
return 1.99;
}
}
public class HouseBlend extends Beverage {
public double code() {
return .89;
}
}
public class Mocha extends CodimentDecorator {
Beverage beverage;
public Mocha (Beverage beverage) {
this.beverage = beverage;
}
public double cost() {
// 첨가물의 가격을 더해준다.
return 0.2 + this.beverage.cost();
}
}
public static void main(String args[]) {
Beverage beverage = Esopresso();
// 모카 두번 추가!
beverage = new Mocha(beverage);
beverage = new Mocha(beverage);
Systme.out.println("에소프레소에 모카 두번!" + beverage.cost());
}
객체의 생성하는 것을 캡슐화하는 패턴을 factory 라고 한다. 특히 서브클래스에서 어떤 객체를 만들지 강제하는 것을 factory method 패턴이라고 한다.
concrete class를 많이 사용하면 새로운 concrete class 를 추가 할 때마다 코드를 고쳐야 하는 경우가 있다. 즉 변화에 닫혀있는 코드가 된다.
Duck duck
if(sth) {
duck = new MallardDuck();
} else {
duck = new RubberDuck();
}
// factoray method pattern
public abstract class PizzaStore {
public Pizza orderPizza(String type){
Pizza pizza;
pizza = createPizza(type);
pizza.prepare();
return pizza;
}
// 서브클래스에서 객체 생성하는 작업을 캡슐화 한다.
protected abstarctPizza createPizza(String type);
}
의존성 뒤집기:
해당 클래스의 인스턴스가 하나만 만들어지고, 어디서든지 그 인스턴스에 접근할 수 있도록 하기 위한 패턴.
// simple singleton
public class Singleton {
private static Singleton uniqueInstance;
// 외부에서 생성자 호출를 막는다.
private Singleton() {};
// 'synchronized' 를 사용하여 thread safe 한 코드를 만든다.
// uniqueInstance가 없을 때 멀티 쓰레드가 동시에 getInstance 를 호출하면 객체가 여러개 생길 수 있다.
public static synchronized Singleton getInstance(){
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
}
// synchronized를 이용한 method 동기화의 오버헤드를 부담이 되는 경우라면, 처음 class가 load 될 때 생성 할 수 있다.
public class SingletonWithoutSyncronizedMethod {
// JVM 에서는 class loader 마다 서로다른 네임스페이스를 정의 하기 때문에,
// 클래스가 여러번 로드되어 싱글턴이 여러개 만들어질 수 있으니 클래스 로더를 조심히 살피자.
private static SingletonWithoutSyncronizedMethod uniqueInstance = new SingletonWithoutSyncronizedMethod();
// 외부에서 생성자 호출를 막는다.
private SingletonWithoutSyncronizedMethod() {};.
public static synchronized SingletonWithoutSyncronizedMethod getInstance(){
return uniqueInstance;
}
}
요청을 객체의 형태로 캡슐화하여 사용자가 보낸 요청을 나중에 이용할 수 있도록 매서드 이름, 매개변수 등 요청에 필요한 정보를 저장 또는 로깅, 취소할 수 있게 하는 패턴이다.
커맨드 패턴에는 명령(command), 수신자(receiver), 발동자(invoker), 클라이언트(client)의 네개의 용어가 항상 따른다.
/*the Invoker class*/
public class Switch {
private Command flipUpCommand;
private Command flipDownCommand;
public Switch(Command flipUpCmd,Command flipDownCmd){
this.flipUpCommand=flipUpCmd;
this.flipDownCommand=flipDownCmd;
}
public void flipUp(){
flipUpCommand.execute();
}
public void flipDown(){
flipDownCommand.execute();
}
}
/*Receiver class*/
public class Light{
public Light(){ }
public void turnOn(){
System.out.println("The light is on");
}
public void turnOff(){
System.out.println("The light is off");
}
}
/*the Command interface*/
public interface Command{
void execute();
}
public class TurnOnLightCommand implements Command{
private Light theLight;
public TurnOnLightCommand(Light light){
this.theLight=light;
}
public void execute(){
theLight.turnOn();
}
}
/*The test class*/
public class TestCommand{
public static void main(String[] args){
Light light=new Light();
Command switchUp=new TurnOnLightCommand(light);
Command switchDown=new TurnOffLightCommand(light);
Switch s=new Switch(switchUp,switchDown);
s.flipUp();
s.flipDown();
}
}
한 클래스의 인터페이스를 클라이언트에서 사용하고자하는 다른 인터페이스로 변환한다. 어댑터를 이용하면 인터페이스 호환성 문제 때문에 같이 쓸 수 없는 클래스들을 연결해서 쓸 수 있다.
public interface Duck {
public void quack();
public void fly();
}
public class MallardDuck implements Duck {
public void quack() {
System.out.println("Quack");
}
public void fly() {
System.out.println("I'm flying");
}
}
public interface Turkey {
public void gobble();
public void fly();
}
public class TurkeyAdapter implements Duck {
Turkey turkey;
public TurkeyAdapter(Turkey turkey) {
this.turkey = turkey;
}
public void quack(){
turkey.gobble();
}
public void fly() {
turkey.fly();
}
}
어떤 서브세스템의 일련의 인터페이스에 대한 통합된 인터페이스를 제공합니다. 퍼사드에서 고수준 인터페이스를 정의하기 때문에 서브시스템을 더 쉽게 사용할 수 있습니다.
class CPU {
public void freeze() { ... }
public void jump(long position) { ... }
public void execute() { ... }
}
class HardDrive {
public byte[] read(long lba, int size) { ... }
}
class Memory {
public void load(long position, byte[] data) { ... }
}
/* Facade */
class ComputerFacade {
private CPU processor;
private Memory ram;
private HardDrive hd;
public ComputerFacade() {
this.processor = new CPU();
this.ram = new Memory();
this.hd = new HardDrive();
}
public void start() {
processor.freeze();
ram.load(BOOT_ADDRESS, hd.read(BOOT_SECTOR, SECTOR_SIZE));
processor.jump(BOOT_ADDRESS);
processor.execute();
}
}
/* Client */
class You {
public static void main(String[] args) {
ComputerFacade computer = new ComputerFacade();
computer.start();
}
}
알고리즘의 골격을 정의합니다. 알고리즘의 여러 단계 중 일부는 서브클래스에서 구현할 수 있습니다. 템플릿 메소드를 이용하면 알고리즘의 구조는 그대로 유지하면서 서브클래스에서 특정 단계를 재정의할 수 있습니다.
public abstract class Beverage {
final vodi prepare() {
boilWater();
brew();
pourInCup()
}
abstract void brew();
void boilWater() {
System.out.println("boilWater");
}
void pourInCup() {
System.out.println("pourInCup");
}
}
public class Tea extends Beverage {
public void brew() {
System.out.println("Tea brew");
}
}
public class Coffee extends Beverage {
public void brew() {
System.out.println("Coffee brew");
}
}
컬렉션 구현 방법을 노출시키지 않으면서도 그 집합체 안에 들어있는 모든 항목에 접근할 수 있게 해주는 방법을 제공해 줍니다.
// 참고로 java.util.Iterator 인터페이스가 있다.
public interface Iterator {
boolean hasNext();
Object next();
}
public class DinerMenuIterator implements Iterator {
MenuItem[] items;
int position = 0;
public DinerMenuIterator(MenuItem[] items) {
this.items = items;
}
public MenuItem next() {
MenuItem menuItem = items[position];
position = position + 1;
return menuItem;
}
public boolean hasNext() {
if (position >= items.length || items[position] == null) {
return false;
} else {
return true;
}
}
}
public class PancakeHouseMenuIterator implements Iterator {
ArrayList<MenuItem> items;
int position = 0;
public PancakeHouseMenuIterator(ArrayList<MenuItem> items) {
this.items = items;
}
public MenuItem next() {
MenuItem item = items.get(position);
position = position + 1;
return item;
}
public boolean hasNext() {
if (position >= items.size()) {
return false;
} else {
return true;
}
}
}
객체들을 트리 구조로 구성하여 부분과 전체를 나타내는 계층구조로 만들 수 있습니다. 이 패턴을 이용하면 클라이언트에서 개별 객체와 다른 객체들로 구성된 복합 객체를 똑같은 방법으로 다룰 수 있습니다.
from abc import ABC, abstractmethod
NOT_IMPLEMENTED = "You should implement this."
class Graphic(ABC):
@abstractmethod
def print(self):
raise NotImplementedError(NOT_IMPLEMENTED)
class CompositeGraphic(Graphic):
def __init__(self):
self.graphics = []
def print(self):
for graphic in self.graphics:
graphic.print()
def add(self, graphic):
self.graphics.append(graphic)
def remove(self, graphic):
self.graphics.remove(graphic)
class Ellipse(Graphic):
def __init__(self, name):
self.name = name
def print(self):
print("Ellipse:", self.name)
ellipse1 = Ellipse("1")
ellipse2 = Ellipse("2")
ellipse3 = Ellipse("3")
ellipse4 = Ellipse("4")
graphic = CompositeGraphic()
graphic1 = CompositeGraphic()
graphic2 = CompositeGraphic()
graphic1.add(ellipse1)
graphic1.add(ellipse2)
graphic1.add(ellipse3)
graphic2.add(ellipse4)
graphic.add(graphic1)
graphic.add(graphic2)
graphic.print()