command pattern

|

Command 요청과 실행을 분리하자!

Command의 요청, 실행을 분리하면서 의존성을 크게 낮춘 모델. command를 abstract class 또는 interface로 만들고 concrete command를 구현한다. Receiver는 실제 Action을 가지고있는 객체이며 command는 이를 사용하여 Action을 만든다. Client가 Invoker에게 command를 세팅하면 추후에 Invoker에게 실행을 요청하며 실행된다.

Overview

sequence-100158873-orig.gif

Code

from abc import ABCMeta, abstractmethod

# Receiver
class Light:
    def on(self):
        print("Light On")

    def off(self):
        print("Light Off")

# Command
class Command:
    __metaclass__ = ABCMeta

    @abstractmethod
    def execute(self):
        pass

    @abstractmethod
    def undo(self):
        pass

# Concrete Command
class LightOnCommand(Command):
    def __init__(self, light):
        self.light = light

    def execute(self):
        self.light.on()

    def undo(self):
        self.light.off()

# Invoker
class SimpleRemoteControl:
    def SetCommand(self, command):
        self.slot = command

    def ButtonPressed(self):
        self.slot.execute()

if __name__ == '__main__':
    controller = SimpleRemoteControl()
    controller.SetCommand(LightOnCommand(Light()))
    controller.ButtonPressed()

__str__, __repr__ 차이

|

용법의 차이

  • str : 사용자가 보기 쉬운 형태로 보여줄 때 사용하는 것
  • repr : 시스템(python interpreter)이 해당 객체를 인식할 수 있는 공식적인 문자열로 나타내 줄 때 사용하는 것

호출 경로

print(a)하면 __str__이 불린다. print([a])하면 __repr__이 불린다.

__repr__만 만들면 __str__이 호출될 시에 __repr__이 불린다. 이는 __str__의 기본 구현이 __repr__을 부르는 구조라서 그렇다고한다.

쓰는 경우

나의 경우 __str__은 디버깅용 로그를 작성할 때, __repr__은 위의 용법에 최대한 맞추되, list를 가진 class이거나 한 경우 그냥 디버깅용으로 쓴다. 이 부분은 더 공부가 필요해보인다.

class dataSchema(object):
    def __init__(self, name: str, age: int):
        self.setProperty(name, age)

    def setProperty(self, name: str, age: int):
        self.name = name
        self.age  = age

    def getProperty(self):
        if self.age == -1:
            raise Exception('not setted.')
        return {'name': self.name, 'age': self.age}

    def __str__(self):
        return "(name : {}, age : {})".format(self.name, self.age)

    def __repr__(self):
        return "{}(\"{}\", {})".format(self.__class__.__name__, self.name, self.age)

decorator pattern

|

Decorator Pattern ?

주어진 상황에 따라 동적으로 어떤 객체에 책임을 붙일 수 있는 패턴으로, 상속 대신 쓸 수 있는 대안이 된다. python에는 @decorator를 통해 비슷한 일을 해줄 수 있다.

Overview

decorators_fig1.jpg 요렇게 생겼다.

  • 최상위에 interface가 존재하며,
  • concrete component들은 그 interface를 구현한 구현체이다.
  • decorator는 interface를 상속받고 interface를 가진다.
  • 그리고 자신의 abstract method들을 구현할 때 가지고있는 interface를 이용하여 구현한다.

말로 쓰니까 어려워보인다. 코드를 넣자!

코드

from abc import ABCMeta, abstractmethod

# Interface
class Widget:
    __metaclass__ = ABCMeta
 
    @abstractmethod
    def draw(self):
        pass

# 구현체
class ConcreteWindow(Widget):
    def __init__(self, w : int, h: int):
        self.mSize = (w, h)

    def draw(self):
        print("my draw size is {}".format(self.mSize))

# decorator
class Decorator(Widget):
    def __init__(self, widget: Widget):
        self.mChildWindow = widget

    def draw(self):
        self.mChildWindow.draw()

class VScrollDecorator(Decorator):
    def draw(self):
        self.mChildWindow.draw()
        print("Draw Vertical Scrollbar")

class HScrollDecorator(Decorator):
    def draw(self):
        self.mChildWindow.draw()
        print("Draw Horizontal Scrollbar")


if __name__ == '__main__':
    testWindow = VScrollDecorator(HScrollDecorator(ConcreteWindow(200,100)))
    testWindow.draw()

output

my draw size is (200, 100)
Draw Horizontal Scrollbar
Draw Vertical Scrollbar

observer pattern

|

Overview

다음은 내맘대로 그려본 Observer pattern 의 시나리오이다.

Observer

생각해보니 MVC패턴에 적용하면 Controller를 Observable, View를 Observer로 놓을 수 있겠다. 다음은 코드이다.

class Observable(object):
 
    def __init__(self):
        self.observers = []
 
    def register(self, observer):
        if not observer in self.observers:
            self.observers.append(observer)
 
    def unregister(self, observer):
        if observer in self.observers:
            self.observers.remove(observer)
 
    def unregister_all(self):
        if self.observers:
            del self.observers[:]  # list의 모든 항목을 지우는 코드. list는 남는다.
 
    def update_observers(self, *args, **kwargs):
        for observer in self.observers:
            observer.update(*args, **kwargs)

from abc import ABCMeta, abstractmethod
 
class Observer(object):
    __metaclass__ = ABCMeta
 
    @abstractmethod
    def update(self, *args, **kwargs):
        pass

from Observable import Observable
from Observer import Observer
 
 
class AmericanStockMarket(Observer):
    def update(self, *args, **kwargs):
        print("American stock market received: {0}\n{1}".format(args, kwargs))
 
 
class EuropeanStockMarket(Observer):
    def update(self, *args, **kwargs):
        print("European stock market received: {0}\n{1}".format(args, kwargs))
 
 
if __name__ == "__main__":
    observable = Observable()
 
    american_observer = AmericanStockMarket()
    observable.register(american_observer)
 
    european_observer = EuropeanStockMarket()
    observable.register(european_observer)
 
    observable.update_observers('Market Rally', something='Hello World')

Strategy pattern

|

Strategy pattern

알고리즘군을 정의하고 각각 캡슐화하여 바꿔쓸 수 있게 만든 패턴. 알고리즘을 사용하는 클라이언트와 독립적으로 알고리즘이 변경 가능하다.

어떻게쓰나?

다수의 interface를 가지는 abstract class가 존재하고. 해당 class를 상속받아서 class를 디자인하는 방법이다.

overView.png

code

코드는 헤드퍼스트에 나온 Duck 예제를 python으로 바꿔서 비슷하게 만들어보았다. 최종 객체가 해야할 일은 다음과 같다.

  • 디스플레이용 함수들
    • performQuack
    • performFly
    • print
  • **위 세가지 함수 중 perform는 여러 객체들이 겹칠 수 있다.**
  • setFlyBehavior, setQuackBehavior로 행동을 바꿀 수 있다. (optional)

print 함수의 경우 모든 상속받은 class에서 다르게 구현되어야 하기에 interface로 빼지 않았다.

Interface

# FlyBehavior.py
from abc import ABCMeta, abstractmethod

class FlyBehavior:
    __metaclass__ = ABCMeta

    @abstractmethod
    def fly(self):
        pass

class FlyWithWings(FlyBehavior):
    def fly(self):
        print("I can fly!")

class FlyNoWay(FlyBehavior):
    def fly(self):
        print("I can't fly.....:(")
# QuackBehavior.py
from abc import ABCMeta, abstractmethod

class QuackBehavior:
    __metaclass__ = ABCMeta

    @abstractmethod
    def quack(self):
        pass

class Quack(QuackBehavior):
    def quack(self):
        print("Quack! Quack!")

class Mute(QuackBehavior):
    def quack(self):
        print(".....")

Abstract Class

# Duck.py
from abc import ABCMeta, abstractmethod

class Duck:
    __metaclass__ = ABCMeta

    def __init__(self):
        self._flyBehavior = None
        self._quackBehavior = None

    @abstractmethod
    def print(self):
        pass

    def performFly(self):
        self._flyBehavior.fly()

    def performQuack(self):
        self._quackBehavior.quack()

    def setFlyBehavior(self, flyBehavior):
        self._flyBehavior = flyBehavior

    def setQuackBehavior(self, quackBehavior):
        self._quackBehavior = quackBehavior

Classes

# RubberDuck.py
from Duck import Duck
from FlyBehavior import FlyNoWay
from QuackBehavior import Quack

class RubberDuck(Duck):
    def __init__(self):
        self._flyBehavior = FlyNoWay()
        self._quackBehavior = Quack()

    def print(self):
        print("I'm rubber duck!")

    def performFly(self):
        self._flyBehavior.fly()

    def performQuack(self):
        self._quackBehavior.quack()

# RealDuck.py
from Duck import Duck
from FlyBehavior import FlyWithWings
from QuackBehavior import Quack

class RealDuck(Duck):
    def __init__(self):
        self._flyBehavior = FlyWithWings()
        self._quackBehavior = Quack()

    def print(self):
        print("I'm real duck!")

    def performFly(self):
        self._flyBehavior.fly()

    def performQuack(self):
        self._quackBehavior.quack()

Test code

from RubberDuck import RubberDuck
from RealDuck import RealDuck

from FlyBehavior import FlyNoWay

if __name__ == "__main__":
    a = RealDuck()
    a.print()
    a.performFly()
    a.performQuack()

    a.setFlyBehavior(FlyNoWay()) # 동적으로 interface를 변경가능!!
    print("I'm wounded!")
    a.performFly()
    a.performQuack()

    print("====================")

    b = RubberDuck()
    b.print()
    b.performFly()
    b.performQuack()

Results

$ python main.py 
I'm real duck!
I can fly!
Quack! Quack!
I'm wounded!
I can't fly.....:(
Quack! Quack!
====================
I'm rubber duck!
I can't fly.....:(
Quack! Quack!