在软件开发中, 设计模式是针对特定上下文中常见问题的解决方案,这些计划都是通过验证的。
他们的次要指标是向咱们展现编程的好办法并解释为什么其余选项不起作用。
应用常见的设计模式,你能够:
- 放慢开发过程;
- 缩小代码行数;
- 确保你的代码设计良好;
- 预感将来由小问题引起的问题。
设计模式能够显着改善软件开发人员的生存,而不论他(她)应用的是哪种编程语言。
我采访了 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