http://python-3-patterns-idioms-test.readthedocs.io/en/latest/PatternConcept.html
So the goal of design patterns is to isolate changes in your code. If you look at it this way, you’ve been seeing some design patterns already in this book. For example, inheritance can be thought of as a design pattern (albeit one implemented by the compiler). It allows you to express differences in behavior (that’s the thing that changes) in objects that all have the same interface (that’s what stays the same). Composition can also be considered a pattern, since it allows you to change-dynamically or statically-the objects that implement your class, and thus the way that class works.
Another pattern that appears in Design Patterns is the iterator, which has been implicitly available in for loops from the beginning of the language, and was introduced as an explicit feature in Python 2.2. An iterator allows you to hide the particular implementation of the container as you’re stepping through and selecting the elements one by one. Thus, you can write generic code that performs an operation on all of the elements in a sequence without regard to the way that sequence is built. Thus your generic code can be used with any object that can produce an iterator.
Classifying Patterns
The Design Patterns book discusses 23 different patterns, classified under three purposes (all of which revolve around the particular aspect that can vary). The three purposes are:
Creational : how an object can be created. This often involves isolating the details of object creation so your code isn’t dependent on what types of objects there are and thus doesn’t have to be changed when you add a new type of object. The aforementioned Singleton is classified as a creational pattern, and later in this book you’ll see examples of Factory Method and Prototype.
Structural : designing objects to satisfy particular project constraints. These work with the way objects are connected with other objects to ensure that changes in the system don’t require changes to those connections.
Behavioral : objects that handle particular types of actions within a program. These encapsulate processes that you want to perform, such as interpreting a language, fulfilling a request, moving through a sequence (as in an iterator), or implementing an algorithm. This book contains examples of the Observer and the Visitor patterns.
"""
:패턴 분류
1. 생성 그룹
- 새로운 객체 유형을 추가해도 객체 생성과 관련된 코드 변경은 최소화
- 싱글톤, 팩토리 메써드, 프로토타입 패턴
2. 연결 그룹
- 연결된 객체 간의 관계를 통해 구현된 내용이 시스템이 바뀌어도 그 관계를
변경하지 않아도 됨
3. 동작 그룹
- 특정한 동작을 담당하는 객체가 있을때 변경 사항 때문에 객체를 변경하지
않아도 됨
- 옵저너, 비지터 패턴
"""
"""
Design Patterns
- Same problem yields same solution with minimal midofication of code
- Same problem yields different solutions with unnecessarily extensive modification of code
Benefits of Promoting Consistency
- Decrease in errors
- Increase in detecting errors
- Cost savings
: No need to reinvent the wheel and waste your time
Scaling Up
Software architecture
- Identifying a pattern to be used throughout software consistently
Frameworks
- A collection od design patterns
- Software security
- Web
Coupling and Cohesion
Coupling
- The degree to which your software elements are connected
Cohesion
- The degree of independence
Simplicity and Generality Trade-Offs
Wider adoption
- More functionality
- Too complex
Simplicity
- Learning curve
- Practitioners
"""
Singleton - Borg 접기
# Singleton - Borg
class Borg:
_shared_state = {}
def __init__(self):
self.__dict__ = self._shared_state
class Singleton(Borg):
def __init__(self, val):
Borg.__init__(self)
self.val = val
def __str__(self):
return self.val
x = Singleton('x')
print(x)
y = Singleton('y')
print(y)
print(x._shared_state)
x.new_val = 100
print(y.new_val)
print(y._shared_state)
# 결과
x
y
{'val': 'y'}
100
{'new_val': 100, 'val': 'y'}
접기
Singleton - Simpler 접기
# Singleton - Simpler
class Singleton(object):
__instance = None
def __new__(cls, val):
if Singleton.__instance is None:
Singleton.__instance = object.__new__(cls)
Singleton.__instance.val = val
return Singleton.__instance
s1 = Singleton('s1')
print("call s1: {}".format(s1.val))
s2 = Singleton('s2')
print("call s1: {}".format(s1.val))
# 결과
call s1: s1
call s1: s2
접기
"""
Factory: 객체 생성의 캡슐화
- 객체를 생성하는 부분을 한 곳으로 모은다.
- 만일 코드 전체에 객체를 생성하는 코드가 퍼져있다면
- 새로운 타입을 추가할때마다 전체 코드를 뒤져서
- 새로운 타입이 어떤 영향을 줄 것인지 확인해야 할 것
- 해결책은 코드 전체에 생성하는 코드를 두지 말고
- 공통의 팩토리에서만 객체를 생성하도록 강제하는 것
- 시스템 내의 모든 객체 생성 코드가 이 팩토리를 쓰도록 강제한다면
- 새로운 객체 추가는 팩토리 변경으로만으로도 가능하다.
"""
Simple Factory Method 1 접기
# Simple Factory Method - dict에 모아 넣기
class Apple():
def __init__(self, name, color):
self._name = name
self._color = color
def make(self):
return "juice produced."
def get_name(self):
return self._name
class Lemon():
def __init__(self, name, size):
self._name = name
self._size = size
def make(self):
return "juice produced."
def get_name(self):
return self._name
def get_juice(fruit="apple"):
""" factory method """
juices = dict(apple=Apple("apple", "red"), lemon=Lemon("lemon", "medium"))
return juices[fruit]
f1 = get_juice("apple")
f2 = get_juice("lemon")
for fruit in (f1, f2):
print("{} {}".format(fruit.get_name(), fruit.make()))
# 결과
apple juice produced.
lemon juice produced.
접기
Simple Factory Method 2 접기
# Simple Factory Method - staticmethod 사용하기
class Pet():
@staticmethod
def factory(type):
if type == "Dog": return Dog()
if type == "Cat": return Cat()
class Dog(Pet):
def feed(self): return "dog is eating."
def speak(self): return "Woof!"
class Cat(Pet):
def feed(self): return "cat is eating."
def speak(self): return "Meow!"
def petGen():
types = Pet.__subclasses__()
return [Pet.factory(i.__name__) for i in types]
pets = petGen()
for pet in pets:
print(pet.feed())
print(pet.speak())
# 결과
dog is eating.
Woof!
cat is eating.
Meow!
접기
Abstract Factory - composition 접기
# Abstract Factory - composition
class Dog:
def speak(self):
return "Woof!"
def __str__(self):
return "Dog"
class DogFactory:
def get_pet(self):
return Dog()
def get_food(self):
return "Dog food!"
class PetStore:
def __init__(self, pet_factory = None):
self._pet_factory = pet_factory
def show_pet(self):
pet = self._pet_factory.get_pet()
pet_food = self._pet_factory.get_food()
print("Our pet is '{}' !".format(pet))
print("Our pet says hello by '{}'".format(pet.speak()))
print("Its food is '{}'".format(pet_food))
factory = DogFactory()
shop = PetStore(factory)
shop.show_pet()
# 결과
Our pet is 'Dog' !
Our pet says hello by 'Woof!'
Its food is 'Dog food!'
접기
Polymorphic Factories 접기
# Polymorphic Factories
class AnimalFactory:
_factories = {}
@staticmethod
def addFactory(id, animalFactory):
AnimalFactory._factories[id] = animalFactory
@staticmethod
def createAnimal(id):
if not id in AnimalFactory._factories:
AnimalFactory._factories[id] = eval(id + '.Factory()')
return AnimalFactory._factories[id].create()
class Animal(object): pass
class Dog(Animal):
def speak(self): print("Bow wow!")
class Factory:
def create(self): return Dog()
class Cat(Animal):
def speak(self): print("Meow!")
class Factory:
def create(self): return Cat()
def animalNameGen():
types = Animal.__subclasses__()
return [type.__name__ for type in types]
animal_names = animalNameGen()
animals = [AnimalFactory.createAnimal(name) for name in animal_names]
print(animals)
for animal in animals:
animal.speak()
class Turtle(Animal):
def speak(self): print("...")
class Factory:
def create(self): return Turtle()
animal_names = animalNameGen()
animals = [AnimalFactory.createAnimal(name) for name in animal_names]
print(animals)
# 결과
[<__main__.Dog object at 0x00702C10>, <__main__.Cat object at 0x00702C50>]
Bow wow!
Meow!
{'Cat': <__main__.Cat.Factory object at 0x00702BD0>, 'Dog': <__main__.Dog.Factory object at 0x00702AD0>, 'Turtle': <__main__.Turtle.Factory object at 0x00702CB0>}
접기
Template Method 접기
# Template Method - 기존 메써드를 재사용하면서 일부만 다시 정의
class AppFramework:
def __init__(self):
self.__templateMethod()
def __templateMethod(self):
self.__displaySeperator()
self.play1()
self.play2()
self.__displaySeperator()
def __displaySeperator(self):
print("=====" * 5)
class MyApp(AppFramework):
def play1(self):
print("play1")
def play2(self):
print("play2")
MyApp()
# 결과
=========================
play1
play2
=========================
접기
Proxy 1 접기
"""
Structurally, the difference between Proxy and State is simple: a Proxy has only one implementation, while State has more than one. The application of the patterns is considered (in Design Patterns) to be distinct: Proxy is used to control access to its implementation, while State allows you to change the implementation dynamically. However, if you expand your notion of “controlling access to implementation” then the two fit neatly together.
"""
# Proxy 1 - 주소 바꿔치기
class Impl():
def a(self):
print("Implement.a()")
def b(self):
print("Implement.b()")
def c(self):
print("Implement.c()")
class Proxy():
def __init__(self):
self.__impl = Impl()
def __getattr__(self, attr):
return getattr(self.__impl, attr)
p = Proxy()
p.a(); p.b(); p.c();
# 결과
Implement.a()
Implement.b()
Implement.c()
접기
Proxy 2 접기
# Proxy 2 - 합성, 상태 머신
class Impl():
def __init__(self):
self._state = 'stop'
def start(self):
if self._state is 'stop' or self._state is 'pause':
self._state = 'start'
print("starting.")
elif self._state is 'start':
print("start again")
def stop(self):
if self._state is 'start' or self._state is 'pause':
self._state = 'stop'
print("stopped.")
elif self._state is 'stop':
print('stop again')
def pause(self):
if self._state is 'start' or self._state is 'stop':
self._state = 'pause'
print("paused.")
elif self._state is 'pause':
print("pause again")
class Proxy():
def __init__(self):
self.impl = Impl()
def start(self):
self.impl.start()
def stop(self):
self.impl.stop()
def pause(self):
self.impl.pause()
p1 = Proxy()
p1.pause()
p1.pause()
p1.start()
p1.stop()
p1.start()
p1.start()
# 결과
paused.
pause again
starting.
stopped.
starting.
start again
접기
Command 접기
"""
- 메써드를 객체로 감싸 다른 메써드나 객체에 파라미터로 던짐
- 객체를 리스트에 담아 공통 메써드를 객체를 바꿔가며 실행하는 것과 유사하나
- 여러 메써드 이름이 아닌 execute라는 공통 메써드 이름을 사용
- Command 클래스는 사실 불필요하다. 파이썬에서는 인터페이스 상속이 아닌
- 구현 상속을 하기 때문에 실행시점에 타입을 체크한다.
- 하지만 각 클래스들 간에 연관 관계가 있다는 점을 분명히 하고자 불필요한
- 상속을 하고 있다.
"""
# Command
class Command():
def execute(self): pass
class Tank(Command):
def execute(self): print("tank drives")
class Battleship(Command):
def execute(self): print("battleship sails")
class Airforce(Command):
def execute(self): print("airforce flys")
class CommandQueue():
_commands = []
def add(self, command):
self._commands.append(command)
def execute(self):
for c in self._commands:
c.execute()
cq = CommandQueue()
cq.add(Tank())
cq.add(Battleship())
cq.add(Airforce())
cq.execute()
# 결과
tank drives
battleship sails
airforce flys
접기
Strategy 접기
# Strategy
class Actions():
def execute(self): pass
class Play():
def execute(self):
print('play')
class Sleep():
def execute(self):
print('sleep')
class Eat():
def execute(self):
print('eat')
class Control():
def __init__(self, strategy=Play()):
self._strategy = strategy
def caller(self):
self._strategy.execute()
def set_action(self, newStrategy):
self._strategy = newStrategy
c = Control()
c.caller()
c.set_action(Sleep())
c.caller()
c.set_action(Eat())
c.caller()
# 결과
play
sleep
eat
접기
Façade 접기
"""
- 구체적인 클래스 구현 내용은 숨기고 싶을때
- 인터페이스만 제공하고 구현에 대해서는 몰라도 되는 상황을 위해
"""
# Façade
class PlusOne():
def __init__(self, val):
self._val = val
def get_value(self):
self._val += 1
return self._val
class MinusOne():
def __init__(self, val):
self._val = val
def get_value(self):
self._val -= 1
return self._val
class Facade():
@staticmethod
def make_PlusOne(val):
return PlusOne(val)
@staticmethod
def make_MinusOne(val):
return MinusOne(val)
f = Facade()
print(f.make_MinusOne(3).get_value())
print(f.make_PlusOne(5).get_value())
# 결과
2
6
접기
Builder 접기
# Builder
class Order():
def __init__(self, builder):
self._builder = builder
def construct(self):
self._builder.create_new_data()
self._builder.add_name()
self._builder.add_color()
self._builder.add_size()
def get_data(self):
return self._builder.data
class AbstractBuilder():
def __int__(self):
self.data = None
def create_new_data(self):
self.data = Data()
class ConcreteBuilder(AbstractBuilder):
def add_name(self):
self.data.name = "string"
def add_color(self):
self.data.color = "black"
def add_size(self):
self.data.size = "10"
class Data():
def __init__(self):
self.name = None
self.color = None
self.size = None
def __str__(self):
return '{} | {} | {}'.format(self.name, self.color, self.size)
b = ConcreteBuilder()
o = Order(b)
o.construct()
d = o.get_data()
print(d)
# 결과
string | black | 10
접기
Prototype 접기
"""
- 객체 생성에 비용이 많이 드는 경우
- 하나를 생성해 두고 그것을 복사할때 사용
"""
# Prototype
from copy import deepcopy
class Prototype():
def __init__(self):
self._objects = {}
def register_object(self, name, obj):
self._objects[name] = obj
def unregister_object(self, name):
del self._objects[name]
def clone(self, name, **attr):
obj = deepcopy(self._objects.get(name))
obj.__dict__.update(**attr)
return obj
class Data():
def __init__(self):
self.name = "string"
self.color = "black"
self.size = "10"
def __str__(self):
return '{} | {} | {}'.format(self.name, self.color, self.size)
d = Data()
prototype = Prototype()
prototype.register_object('data01', d)
d1 = prototype.clone('data01')
print(d1)
# 결과
string | black | 10
접기
Prototype - my version 접기
# Prototype
""" 복수개의 객체를 담아 사본을 만든다. """
from copy import deepcopy
class Prototype():
def __init__(self):
self._objects = {}
def register_obj(self, name, obj):
self._objects[name] = obj
def del_obj(self, name):
# dict의 key 참조 삭제의 경우 없는 item에 대해서 에러가 발생하므로 pop 메써드로 바꾼다.
self._objects.pop(name, None)
# del self._objects[name]
def clone_obj(self, name, **attr):
# dict의 key 참조를 사용하면 없는 item에 대해서 에러가 발생하므로 get 메써드로 바꾼다.
new_obj = deepcopy(self._objects.get(name))
new_obj.__dict__.update(**attr)
return new_obj
class Data():
def __init__(self):
self.name = 'name'
self.color = 'color'
self.size = 'size'
def __str__(self):
return '{} | {} | {}'.format(self.name, self.name, self.size)
p = Prototype()
d = Data()
p.register_obj('obj01', d)
d1 = p.clone_obj('obj01', size='medium')
print(d1)
# 결과
name | name | medium
접기
Decorator
- New features to an existing object
- Dynamic changes
- Not using subclassing
Decorator: Dynamic Type Selection 접기
# Decorator: Dynamic Type Selection
class AddComponent:
def getSum(self):
return self.__class__.value
def getDescription(self):
return self.__class__.__name__
class Base(AddComponent):
value = 0
class Decorator(AddComponent):
def __init__(self, addComponent):
self.component = addComponent
def getSum(self):
return self.component.getSum() + AddComponent.getSum(self)
def getDescription(self):
return self.component.getDescription() + ' ' + AddComponent.getDescription(self)
class PlusOne(Decorator):
value = 1
def __init__(self, addComponent):
Decorator.__init__(self, addComponent)
class PlusTwo(Decorator):
value = 2
def __init__(self, addComponent):
Decorator.__init__(self, addComponent)
finalSum = PlusTwo(PlusOne(Base()))
print(finalSum.getSum())
print(finalSum.getDescription())
# 결과
3
Base PlusOne PlusTwo
접기
Multiple Dispatching 접기
# Multiple Dispatching
class Outcome:
def __init__(self, name):
self.name = name
def __str__(self):
return self.name
Outcome.WIN = Outcome("WIN")
Outcome.LOSE = Outcome("LOSE")
Outcome.DRAW = Outcome("DRAW")
class Item:
def __str__(self):
return self.__class__.__name__
class Paper(Item):
def compete(self, item):
return item.evalPaper(self)
def evalPaper(self, item):
return Outcome.DRAW
def evalScissors(self, item):
return Outcome.WIN
def evalRock(self, item):
return Outcome.LOSE
class Scissors(Item):
def compete(self, item):
return item.evalScissors(self)
def evalPaper(self, item):
return Outcome.LOSE
def evalScissors(self, item):
return Outcome.DRAW
def evalRock(self, item):
return Outcome.WIN
class Rock(Item):
def compete(self, item):
return item.evalRock(self)
def evalPaper(self, item):
return Outcome.WIN
def evalScissors(self, item):
return Outcome.LOSE
def evalRock(self, item):
return Outcome.DRAW
def match(item1, item2):
print("%s <-> %s: %s" % (item1, item2, item1.compete(item2)))
p = Paper()
s = Scissors()
r = Rock()
match(p, s)
match(r, s)
match(r, r)
match(s, p)
# 결과
Paper <-> Scissors: LOSE
Rock <-> Scissors: WIN
Rock <-> Rock: DRAW
Scissors <-> Paper: WIN
접기
P, S, R another version 접기
# another version - table lookup
class Outcome:
def __init__(self, name):
self.name = name
def __str__(self):
return self.name
Outcome.WIN = Outcome("WIN")
Outcome.LOSE = Outcome("LOSE")
Outcome.DRAW = Outcome("DRAW")
class Item:
def compete(self, item):
return outcome[self.__class__, item.__class__]
def __str__(self):
return self.__class__.__name__
class Paper(Item): pass
class Scissors(Item): pass
class Rock(Item): pass
outcome = {
(Paper, Rock): Outcome.WIN,
(Paper, Scissors): Outcome.LOSE,
(Paper, Paper): Outcome.DRAW,
(Scissors, Paper): Outcome.WIN,
(Scissors, Rock): Outcome.LOSE,
(Scissors, Scissors): Outcome.DRAW,
(Rock, Scissors): Outcome.WIN,
(Rock, Paper): Outcome.LOSE,
(Rock, Rock): Outcome.DRAW,
}
def match(item1, item2):
print("%s <-> %s: %s" % (item1, item2, item1.compete(item2)))
p = Paper()
s = Scissors()
r = Rock()
# match(p, s)
# match(r, s)
# match(r, r)
match(s, p)
# 결과
Scissors <-> Paper: WIN
접기
p, s, r my version 접기
# p s r my version
class Outcome:
def __init__(self):
self.WIN = "WIN"
self.LOSE = "LOSE"
self.DRAW = "DRAW"
self.TABLE = {
(Paper, Rock): self.WIN,
(Paper, Scissors): self.LOSE,
(Paper, Paper): self.DRAW,
(Scissors, Paper): self.WIN,
(Scissors, Rock): self.LOSE,
(Scissors, Scissors): self.DRAW,
(Rock, Scissors): self.WIN,
(Rock, Paper): self.LOSE,
(Rock, Rock): self.DRAW,
}
def __str__(self):
return self.name
class Item:
def __init__(self):
self.outcome = Outcome()
def compete(self, other):
return self.outcome.TABLE[self.__class__, other.__class__]
def match(self, item1, item2):
print("%s <-> %s: %s" % (item1, item2, item1.compete(item2)))
def __str__(self):
return self.__class__.__name__
class Paper(Item): pass
class Scissors(Item): pass
class Rock(Item): pass
p = Paper()
s = Scissors()
r = Rock()
p.match(p, r)
p.match(r, r)
p.match(s, r)
# 결과
Paper <-> Rock: WIN
Rock <-> Rock: DRAW
Scissors <-> Rock: LOSE
접기
Bridge 접기
# Bridge
class NormalDisplay:
def print_it(self, text):
print("Normal-quality print: {}".format(text))
class HighDisplay:
def print_it(self, text):
print("High-quality print: {}".format(text))
class Display:
def __init__(self, text, apiver):
self.text = text
self.apiver = apiver
def display(self):
self.apiver.print_it(self.text)
d1 = Display("hi hello", NormalDisplay())
d1.display()
d2 = Display("hi hello", HighDisplay())
d2.display()
# 결과
Normal-quality print: hi hello
High-quality print: hi hello
접기
Composite 접기
# Composite
class Component:
def __init__(self, *args, **kwargs): pass
def component_function(self): pass
class Child(Component):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.name = args[0]
def component_function(self):
print("{}".format(self.name))
class Composite(Component):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.name = args[0]
self.children = []
def append_child(self, child):
self.children.append(child)
def remove_child(self, child):
self.children.remove(child)
def component_function(self):
print("{}".format(self.name))
for c in self.children:
c.component_function()
#Build a composite submenu 1
sub1 = Composite("submenu1")
#Create a new child sub_submenu 11
sub11 = Child("sub_submenu 11")
#Create a new Child sub_submenu 12
sub12 = Child("sub_submenu 12")
#Add the sub_submenu 11 to submenu 1
sub1.append_child(sub11)
#Add the sub_submenu 12 to submenu 1
sub1.append_child(sub12)
#Build a top-level composite menu
top = Composite("top_menu")
#Build a submenu 2 that is not a composite
sub2 = Child("submenu2")
#Add the composite submenu 1 to the top-level composite menu
top.append_child(sub1)
#Add the plain submenu 2 to the top-level composite menu
top.append_child(sub2)
#Let's test if our Composite pattern works!
top.component_function()
# 결과
top_menu
submenu1
sub_submenu 11
sub_submenu 12
submenu2
접기
Observer 접기
# Observer
class Component():
def __init__(self):
self._observers = []
def add(self, observer):
if observer not in self._observers:
self._observers.append(observer)
def remove(self, observer):
try:
self._observers.remove(observer)
except ValueError:
pass
def notify(self):
for observer in self._observers:
observer.update(self)
class Concrete(Component):
def __init__(self, name = ''):
Component.__init__(self)
self.name = name
self._value = 0
@property
def value(self):
return self._value
@value.setter
def value(self, value):
self._value = value
self.notify()
class Viewer:
def __init__(self, name):
self._name = name
def update(self, observer):
print("{}: {}".format(self._name, observer.value))
c1 = Concrete("Machine 1")
c2 = Concrete("Machine 2")
v1 = Viewer("Viewer 1")
v2 = Viewer("Viewer 2")
c1.add(v1)
c1.add(v2)
c1.value = 1
c1.value = 2
# 결과
Viewer 1: 1
Viewer 2: 1
Viewer 1: 2
Viewer 2: 2
접기
Visitor 접기
# Visitor
"""
- New Operations
- Existing classes
- All dynamically done
"""
class Visitable:
def accept(self, visitor):
visitor.visit(self)
def add(self, adder):
print(self, "worked on by", adder)
def divide(self, divider):
print(self, "worked on by", divider)
def __str__(self):
return self.__class__.__name__
class Visitor:
def __str__(self):
return self.__class__.__name__
class Adder(Visitor):
def visit(self, visitable):
visitable.add(self)
class Divider(Visitor):
def visit(self, visitable):
visitable.divide(self)
a = Adder()
d = Divider()
v = Visitable()
v.accept(a)
v.accept(d)
# 결과
Visitable worked on by Adder
Visitable worked on by Divider
접기
Chain of responsibility 접기
# Chain of responsibility
class Handler:
def __init__(self, next):
self._next = next
def handle(self, request):
handled = self._handle(request)
if not handled:
self._next.handle(request)
def _handle(self, request):
raise NotImplementedError('Must provide implementation is subclass!')
class ConcreteHandler(Handler):
def _handle(self, request):
if 0 < request <= 10:
print("Request {} handled in ConcreteHandler".format(request))
return True
class DefaultHandler(Handler):
def _handle(self, request):
print("End of chain, no handler for {}".format(request))
return True
class Client:
def __init__(self):
self.handler = ConcreteHandler(DefaultHandler(None))
def delegate(self, request):
for request in requests:
self.handler.handle(request)
c = Client()
requests = [1, 5, 11]
c.delegate(requests)
# 결과
Request 1 handled in ConcreteHandler
Request 5 handled in ConcreteHandler
End of chain, no handler for 11
접기
댓글 영역