在软件开发中,设计模式是针对特定上下文中常见问题的解决方案,这些方案都是经过验证的。
他们的主要目标是向我们展示编程的好方法并解释为什么其他选项不起作用。
使用常见的设计模式,你可以:
- 加快开发进程;
- 减少代码行数;
- 确保你的代码设计良好;
- 预见未来由小问题引起的问题。
设计模式可以显着改善软件开发人员的生活,而不管他(她)使用的是哪种编程语言。
我采访了 Jellyfish.tech 的创始人兼首席技术官、拥有 9 年以上经验的 Python 开发人员和软件架构师 Roman Latyshenko 关于他的顶级设计模式。
我主要使用 Python/Django,所以这里是我在工作中每天使用的 Python 中的设计模式列表。
行为型
迭代器模式
迭代器允许遍历集合的元素而不暴露内部细节。
使用场景。 大多数情况下,我使用它来提供遍历集合的标准方法。
➕ 清洁客户端代码(单一职责原则)。
➕ 可以在不更改客户端代码的情况下在集合中引入迭代器(开放/封闭原则)。
➕ 每个迭代对象都有自己的迭代状态,所以你可以推迟和继续迭代。
➖ 对简单的集合使用迭代器会使应用程序过载。
代码示例
from __future__ import annotations
from collections.abc import Iterable, Iterator
from typing import Any, List
class AlphabeticalOrderIterator(Iterator):
_position: int = None
_reverse: bool = False
def __init__(self, collection: WordsCollection,
reverse: bool = False):
self._collection = collection
self._reverse = reverse
self._position = -1 if reverse else 0
def __next__(self):
try:
value = self._collection[self._position]
self._position += -1 if self._reverse else 1
except IndexError:
raise StopIteration()
return value
class WordsCollection(Iterable):
def __init__(self, collection: List[Any] = []):
self._collection = collection
def __iter__(self) -> AlphabeticalOrderIterator:
return AlphabeticalOrderIterator(self._collection)
def get_reverse_iterator(self) -> AlphabeticalOrderIterator:
return AlphabeticalOrderIterator(self._collection, True)
def add_item(self, item: Any):
self._collection.append(item)
if __name__ == "__main__":
collection = WordsCollection()
collection.add_item("First")
collection.add_item("Second")
collection.add_item("Third")
print("Straight traversal:")
print("\n".join(collection))
print("Reverse traversal:")
print("\n".join(collection.get_reverse_iterator()))
状态模式
状态模式帮助对象在其内部状态发生变化时改变其行为。
使用场景。 状态模式帮助我
- 改变大量的对象状态。
- 减少类似转换和状态中重复代码的行数。
- 避免大量条件。
➕ 遵循单一职责原则:将与不同状态相关的代码的类分开。
➕ 添加新状态时不改变类的上下文或状态(开/闭原则)。
➖ 在状态机几乎没有变化的情况下,使用状态可能会太多。
示例代码
from __future__ import annotations
from abc import ABC, abstractmethod
class Context(ABC):
_state = None
def __init__(self, state: State):
self.transition_to(state)
def transition_to(self, state: State):
print(f"Context: Transition to {type(state).__name__}")
self._state = state
self._state.context = self
def request1(self):
self._state.handle1()
def request2(self):
self._state.handle2()
class State(ABC):
@property
def context(self) -> Context:
return self._context
@context.setter
def context(self, context: Context):
self._context = context
@abstractmethod
def handle1(self):
pass
@abstractmethod
def handle2(self):
pass
class ConcreteStateA(State):
def handle1(self):
print("ConcreteStateA handles request1.")
print("ConcreteStateA wants to change the state of the context.")
self.context.transition_to(ConcreteStateB())
def handle2(self):
print("ConcreteStateA handles request2.")
class ConcreteStateB(State):
def handle1(self):
print("ConcreteStateB handles request1.")
def handle2(self):
print("ConcreteStateB handles request2.")
print("ConcreteStateB wants to change the state of the context.")
self.context.transition_to(ConcreteStateA())
if __name__ == "__main__":
context = Context(ConcreteStateA())
context.request1()
context.request2()
观察者模式
观察者会通知它们观察到的其他对象中发生的事件,而无需耦合到它们的类。
使用场景。 每次我需要添加订阅机制以让对象订阅/取消订阅特定发布者类发生的事件的通知时,我使用观察者模式。
一个很好的例子是简单订阅任何在线杂志的新闻,通常可以选择你感兴趣的领域(科学、数字技术等)。 或者,电子商务平台的“有货时通知我”按钮是另一个例子。
➕ 你不必更改发布者的代码来添加订阅者的类。
➖ 订阅者以随机顺序收到通知。
示例代码
from __future__ import annotations
from abc import ABC, abstractmethod
from random import randrange
from typing import List
class Subject(ABC):
@abstractmethod
def attach(self, observer: Observer):
pass
@abstractmethod
def detach(self, observer: Observer):
pass
@abstractmethod
def notify(self):
pass
class ConcreteSubject(Subject):
_state: int = None
_observers: List[Observer] = []
def attach(self, observer: Observer):
print("Subject: Attached an observer.")
self._observers.append(observer)
def detach(self, observer: Observer):
self._observers.remove(observer)
def notify(self):
print("Subject: Notifying observers...")
for observer in self._observers:
observer.update(self)
def some_business_logic(self):
print("Subject: I'm doing something important.")
self._state = randrange(0, 10)
print(f"Subject: My state has just changed to: {self._state}")
self.notify()
class Observer(ABC):
@abstractmethod
def update(self, subject: Subject):
pass
class ConcreteObserverA(Observer):
def update(self, subject: Subject):
if subject._state < 3:
print("ConcreteObserverA: Reacted to the event")
class ConcreteObserverB(Observer):
def update(self, subject: Subject):
if subject._state == 0 or subject._state >= 2:
print("ConcreteObserverB: Reacted to the event")
if __name__ == "__main__":
subject = ConcreteSubject()
observer_a = ConcreteObserverA()
subject.attach(observer_a)
observer_b = ConcreteObserverB()
subject.attach(observer_b)
subject.some_business_logic()
subject.some_business_logic()
subject.detach(observer_a)
subject.some_business_logic()
结构型
外观模式
外观模式提供了一个简化但有限的界面来降低应用程序的复杂性。 外观模式可以“屏蔽”具有多个活动部件的复杂子系统。
使用场景。 我创建了外观模式类,以防我必须使用复杂的库和 API 和(或)我只需要它们的部分功能。
➕ 系统复杂度与代码分离
➖ 使用外观模式,你可以创建一个上帝对象。
示例代码
class Addition:
def __init__(self, field1: int, field2: int):
self.field1 = field1
self.field2 = field2
def get_result(self):
return self.field1 + self.field2
class Multiplication:
def __init__(self, field1: int, field2: int):
self.field1 = field1
self.field2 = field2
def get_result(self):
return self.field1 * self.field2
class Subtraction:
def __init__(self, field1: int, field2: int):
self.field1 = field1
self.field2 = field2
def get_result(self):
return self.field1 - self.field2
class Facade:
@staticmethod
def make_addition(*args) -> Addition:
return Addition(*args)
@staticmethod
def make_multiplication(*args) -> Multiplication:
return Multiplication(*args)
@staticmethod
def make_subtraction(*args) -> Subtraction:
return Subtraction(*args)
if __name__ == "__main__":
addition_obj = Facade.make_addition(5, 5)
multiplication_obj = Facade.make_multiplication(5, 2)
subtraction_obj = Facade.make_subtraction(15, 5)
print(addition_obj.get_result())
print(multiplication_obj.get_result())
print(subtraction_obj.get_result())
装饰器模式
装饰器将新行为附加到对象而不修改它们的结构。
该模式生成一个装饰器类来包装原始类并添加新功能。
使用场景。 每次我需要向对象添加额外的行为而不进入代码时,我都会使用装饰器模式。
➕ 改变对象行为而不创建子类。
➕ 你可以通过将一个对象包装到多个装饰器中来组合多个行为。
➖ 一个特定的装饰器很难从包装器堆栈中移除。
示例代码
class my_decorator:
def __init__(self, func):
print("inside my_decorator.__init__()")
func() # Prove that function definition has completed
def __call__(self):
print("inside my_decorator.__call__()")
@my_decorator
def my_function():
print("inside my_function()")
if __name__ == "__main__":
my_function()
适配器模式
适配器模式作为中间层类来连接独立或不兼容接口的功能。
使用场景。 设置接口之间的协作,我使用适配器模式来解决格式不兼容的问题。
例如,适配器可以帮助将 XML 数据格式转换为 JSON 以进行进一步分析。
➕ 允许将接口与业务逻辑分离。
➕ 添加新的适配器不会破坏客户端的代码
➖ 增加代码复杂度
示例代码
class Target:
def request(self):
return "Target: The default target's behavior."
class Adaptee:
def specific_request(self):
return ".eetpadA eht fo roivaheb laicepS"
class Adapter(Target, Adaptee):
def request(self):
return f"Adapter: (TRANSLATED) {self.specific_request()[::-1]}"
def client_code(target: "Target"):
print(target.request())
if __name__ == "__main__":
print("Client: I can work just fine with the Target objects:")
target = Target()
client_code(target)
adaptee = Adaptee()
print("Client: The Adaptee class has a weird interface. "
"See, I don't understand it:")
print(f"Adaptee: {adaptee.specific_request()}")
print("Client: But I can work with it via the Adapter:")
adapter = Adapter()
client_code(adapter)
创建型
单例模式
单例模式限制一个类拥有多个实例,并确保该实例的全局访问点。
使用场景。单例模式帮我
- 管理共享资源:即由应用程序的多个部分共享的单个数据库、文件管理器或打印机假脱机程序。
- 存储全局状态(帮助文件路径、用户语言、应用程序路径等)。
- 创建一个简单的 logger。
➕ 类只有一个实例
➖ 很难对代码进行单元测试,因为大多数测试框架在创建 mock 对象时使用继承。
代码示例
class Singleton:
def __new__(cls):
if not hasattr(cls, 'instance'):
cls.instance = super(Singleton, cls).__new__(cls)
return cls.instance
if __name__ == "__main__":
s = Singleton()
print("Object created:", s)
s1 = Singleton()
print("Object created:", s1)
什么时候使用 Python 的设计模式?
当你需要为多个 API 选项提供统一接口时,外观模式非常有用。 例如,你应该在应用程序中集成一个支付系统,留下更改它的可能性。在这种情况下,你可以使用外观模式,你只需创建一个新的外观,而无需重写整个应用程序。
如果 API 完全不同,这里的问题就会出现,因为为外观模式设计通用接口并不是一件容易的事。
状态用于管理应用程序的多个独立组件,前提是初始架构意味着它们的独立性。因此,为状态管理创建一个单独的模块以及使用观察者模式可能是一个好主意。
由于内置对装饰器的支持,装饰器可能是最常用的 Python 模式。例如,装饰器提供了一种方便且明确的方式来使用某些库,并为应用程序设计和管理创造了越来越丰富的机会。该模式还确保了函数组合的广泛可能性,并发现了函数式编程的新机会。
适配器适用于处理大量不同格式的数据。该模式允许对每种数据格式使用一种算法而不是多种算法。
迭代器有类似的好处,所以它们可以一起使用。此外,称为生成器的迭代器变体之一(很久以前在 Python 中引入)允许更有效地使用内存,处理大量数据时对某些类型的项目非常有价值。
最后,单例模式的重要性不容小觑:数据库连接、API、文件……这些都是开发人员应该清楚流程如何避免出错的时刻。而单例模式在这里可以做得很好,更不用说每次使用同一个实例而不是复制它来减少内存消耗的可能性。
翻译
Implementation of Top Design Patterns in Python