4.11 设计模式
在Python中,类的设计模式是指一种通用的解决方案或设计模板,针对特定的问题或需求构建类结构,并提供相关的方法和属性。这些设计模式可以帮助开发人员遵循最佳实践、提高代码质量、增强可读性、降低维护成本。
需要注意的是,类设计模式不是语言特定的,这些模式同样适用于其他面向对象编程语言。
在 Python 中,有多种常见的类设计模式。下面将进行介绍。
4.11.1 工厂模式(Factory Pattern)
用于创建对象实例的模式,简化了对象的创建过程。
在Python中,类设计的工厂模式是一种创建对象的方法。它可以使用一个公共接口来创建不同的对象,这些对象通常共享相同的属性和行为。更具体地说,工厂模式可以通过使用过程化编程技术和面向对象编程技术来实现。
下面是一个示例:
class Dog:
def __init__(self, name):
self._name = name
def speak(self):
return "Woof!"
class Cat:
def __init__(self, name):
self._name = name
def speak(self):
return "Meow!"
def get_pet(pet="dog"):
pets = dict(dog=Dog("Hope"), cat=Cat("Peace"))
return pets[pet]
dog = get_pet("dog")
print(dog.speak())
cat = get_pet("cat")
print(cat.speak())
在上述代码中,我们定义了两个类:Dog和Cat,每个类都有它自己的__init__函数和speak方法。然后我们定义了一个get_pet函数,该函数接收一个参数pet并根据传递的值获取pets字典中对应的实例赋值给dog或者cat变量中。
如果传入的pet参数是"dog",那么将调用get_pet函数,并返回一个Dog对象;而如果传入的参数是"cat",那么会返回一个Cat对象。最后我们分别调用了dog.speak()和cat.speak()方法输出其对应的声音。
这种设计模式对于以下情况非常有用:
- 当我们需要隐藏对象创建的实现细节时。
- 当我们希望将对象的创建与使用分开时。
- 当我们想要通过公共接口在运行时确定对象类型时。
4.11.2 单例模式(Singleton Pattern)
确保类只能有一个实例,并提供对该实例的全局访问点。
在Python中,类设计的单例模式是指一个类只有一个实例对象。这意味着无论如何调用该类,在内存中只会存在同一个实例对象,如果再次创建该类的实例对象时,将返回已经存在的那个。这可以避免在程序中多次创建相同的对象,节省资源和提高性能。
下面是一个示例:
class Singleton:
__instance = None
def __new__(cls, name):
if cls.__instance is None:
cls.__instance = super().__new__(cls)
cls.__instance.__initialized = False
return cls.__instance
def __init__(self, name):
if not self.__initialized:
self.name = name
self.__initialized = True
def say_hello(self):
print(f"Hello, I am {self.name} ({id(self)})!")
在上述代码中,我们定义了一个名为Singleton
的类,使用了双重判断的方式确保仅创建一个实例对象。在类的__new__
方法中,如果没有创建过实例对象,就通过super()
调用父类的__new__
方法来创建一个实例对象,并将它赋值给__instance
属性。若已经创建过实例对象,则直接返回现有的那个实例对象。同时类中的__initialized
属性用于确保__init__
方法只执行一次。
我们还在类的__init__
方法中添加了一个名为name
的属性,以标识该实例的名称。最后我们定义了一个say_hello
方法,用于输出实例的名称和其在内存中的地址。
下面展示如何使用Singleton
类:
dog1 = Singleton("Hope")
dog2 = Singleton("Peace")
print(id(dog1))
print(id(dog2))
dog1.say_hello()
dog2.say_hello()
print(dog1 == dog2)
在上述代码中,我们先创建了两个实例对象dog1
和dog2
,采用不同的name,但是由于是单例模式,只有第一次的name能够正常赋值,dog2的name则无法再次赋值。最后得到的结果就显示dog1和dog2的id一致,say_hello函数返回的也一致。
4.11.3 观察者模式(Observer Pattern)
在对象之间建立一对多的依赖关系,以便当一个对象状态更改时通知其所有依赖项。
在Python中,类设计的观察者模式是指当一个对象的状态发生改变时,所有依赖它的其他对象都会得到通知并自动更新。
下面是一个示例:
class Observer:
def update(self, obj, *args, **kwargs):
pass
class Observable:
def __init__(self):
self._observers = []
def addObserver(self, observer):
if observer not in self._observers:
self._observers.append(observer)
def removeObserver(self, observer):
if observer in self._observers:
self._observers.remove(observer)
def notifyObservers(self, obj, *args, **kwargs):
for observer in self._observers:
observer.update(obj, *args, **kwargs)
class Dog(Observable):
def __init__(self, name="dog"):
super().__init__()
self._name = name
def setName(self, name):
self._name = name
self.notifyObservers(self)
def getName(self):
return self._name
class Owner(Observer):
def __init__(self, name):
self._name = name
def update(self, dog, *args, **kwargs):
print(f"{self._name}: {dog.getName()} seems happy today!")
dog1 = Dog("Hope")
dog2 = Dog("Peace")
owner1 = Owner("Alice")
owner2 = Owner("Bob")
dog1.addObserver(owner1)
dog1.addObserver(owner2)
dog2.addObserver(owner2)
dog1.setName("Happy Hope")
dog2.setName("Peaceful Peace")
在上面的代码中,我们定义了两个类Observer
和Observable
,以及继承自Observable
的Dog
类和继承自Observer
的Owner
类。
Observer
类中定义了名称为update
的方法,这是观察者需要实现的方法。在本例中,我们没有在其中写入任何代码。 Observable
类实现了添加、删除和通知观察者对象的方法,其中addObserver
将要添加的观察者放入到观察者列表中。此时观察者可以根据removeObserver
则是从观察者列表中剔除。notifyObservers
则是通知观察者。
4.11.4 适配器模式(Adapter Pattern)
将接口转换为其他接口,以兼容客户端代码的需求。
适配器模式是一种设计模式,它允许我们将一个类的接口转换为另一个客户端所期望的接口。在Python中,适配器模式的实现方式通常涉及到继承和组合两种方式。
下面是一个使用继承实现适配器模式的示例,假设我们有两个类,一个是Adaptee类,具有不同于目标客户端所期望的接口:
class Adaptee:
def specific_request(self):
return "adaptee code"
和一个客户端所期望的接口Target:
class Target:
def request(self):
pass
我们可以通过TargetClassAdapter类来使Adaptee与Target兼容,适配器将Adaptee的方法调用转换成Target客户端所期望的接口:
class TargetClassAdapter(Target, Adaptee):
def request(self):
return self.specific_request()
这样一来,客户端就可以使用TargetClassAdapter类来调用Adatpee的方法,同时符合Target接口规范:
if __name__ == "__main__":
target = TargetClassAdapter()
result = target.request()
print(result) # 'adaptee code'
同样,我们也可以使用组合方式来实现适配器模式。
4.11.5 组合模式(Composite Pattern)
通过将对象组合成树形结构,使得单个对象和组合对象都可以按统一的方式进行处理。
在Python中,组合模式指的是将对象组合成树形结构以表示“部分-整体”的层次结构。组合能让客户端以一致的方式处理个别对象以及对象组合。
以下是使用Python实现组合模式的简单示例:
class Component:
def __init__(self, name):
self.name = name
def add(self, component):
pass
def remove(self, component):
pass
def display(self, depth):
pass
class Leaf(Component):
def add(self, component):
print("Cannot add to a leaf")
def remove(self, component):
print("Cannot remove from a leaf")
def display(self, depth):
print("-" * depth + self.name)
class Composite(Component):
def __init__(self, name):
super().__init__(name)
self.children = []
def add(self, component):
self.children.append(component)
def remove(self, component):
self.children.remove(component)
def display(self, depth):
print("-" * depth + self.name)
for child in self.children:
child.display(depth + 2)
if __name__ == "__main__":
root = Composite("root")
root.add(Leaf("leaf A"))
root.add(Leaf("leaf B"))
comp = Composite("Composite X")
comp.add(Leaf("leaf XA"))
comp.add(Leaf("leaf XB"))
root.add(comp)
root.display(1)
在这个例子中,Component
类代表组合模式中的组件,其中包含了添加、删除和显示其内容的方法。Leaf
类代表叶节点,不能够包含其他的组件。Composite
类代表组合节点,包含了多个子组件。
在这个示例中,创建了一个root
组合节点,包含两个叶节点Leaf A
和Leaf B
以及一个名为Composite X
的子组合节点。调用display()
方法时,将按树形结构递归地显示所有组件的内容。
4.11.6 策略模式(Strategy Pattern)
定义算法族,使它们之间可以互相替换,而不会影响到客户端的使用。
类的策略模式是一种设计模式,它允许在运行时选择算法的不同变体或行为。这个模式中,我们将不同的算法或策略封装成不同的类并让他们可以相互替换。
以下是一个简单的代码示例:
class Strategy:
def do_algorithm(self, data):
pass
class ConcreteStrategyA(Strategy):
def do_algorithm(self, data):
return sorted(data)
class ConcreteStrategyB(Strategy):
def do_algorithm(self, data):
return list(reversed(sorted(data)))
class Context:
def __init__(self, strategy: Strategy):
self._strategy = strategy
def execute_strategy(self, data):
return self._strategy.do_algorithm(data)
if __name__ == "__main__":
context_a = Context(ConcreteStrategyA())
result_a = context_a.execute_strategy([1, 3, 2])
print(result_a)
context_b = Context(ConcreteStrategyB())
result_b = context_b.execute_strategy([1, 3, 2])
print(result_b)
在这个例子中,我们定义了一个Strategy
的基类和两个具体的策略类:ConcreteStrategyA
和ConcreteStrategyB
。每个策略类都实现了do_algorithm
方法,并分别提供了不同的实现。
接着我们定义了一个上下文类Context
用来执行策略并生成所需的结果。这个类包含一个指向Strategy
对象的引用,并在执行方法时将数据传递给所选的策略类进行处理。
最后我们可以创建不同的上下文对象,并通过方法的多态性执行相应的策略。这样就可以实现运行时动态选择算法行为的目的。
4.11.7 装饰器模式(Decorator Pattern)
动态地向对象添加额外的行为,而无需修改原始类的代码。
在Python中,类设计的装饰器模式是指,使用装饰器来修改一个类的行为或属性,而不必直接修改该类的原始定义。这可以使代码更加灵活和可维护。
以下是一个实例,其中定义了一个名为Logger
的装饰器,它可以添加记录方法到一个类中:
def Logger(cls):
"""A decorator that adds logging functionality to a class"""
# Define the logging method
def log(self, message):
print(f"{self.__class__.__name__}: {message}")
# Add the logging method to the class
cls.log = log
# Return the modified class
return cls
# Define a class with the Logger decorator
@Logger
class MyClass:
pass
# Use the class and its logging method
my_object = MyClass()
my_object.log("Hello World!")
在上面的示例中,Logger
函数作为一个装饰器来使用,用于增加Python类的日志功能。当我们在类定义之前应用此装饰器时,Logger
函数将自动被调用并向该类添加log
方法,这个方法可以访问该类的名称和任何传递给它的消息。因此,在创建MyClass
对象后,我们可以调用该对象的log
方法,并输出一条带有类的名称和消息的日志信息。
4.11.8 建造者模式(Builder Pattern)
将复杂的对象构建与其表示分离,以便不同的表示方式可以用于该对象进行构建。
类设计的建造者模式是一种创建复杂对象的设计模式,它使用多个简单的对象逐步构建出一个复杂的对象。这种模式是将一个复杂对象的构建与其表示分离,使得同样的构建过程可以创建不同的表示。
在Python中,可以用下面的示例演示建造者模式。
class Pizza:
def __init__(self, dough='', sauce='', toppings=[]):
self.dough = dough
self.sauce = sauce
self.toppings = toppings
def __str__(self):
return 'dough: {}, sauce: {}, toppings: {}'.format(self.dough, self.sauce, ', '.join(self.toppings))
class PizzaBuilder:
def __init__(self):
self.pizza = Pizza()
def set_dough(self, dough):
self.pizza.dough = dough
return self
def set_sauce(self, sauce):
self.pizza.sauce = sauce
return self
def add_topping(self, topping):
self.pizza.toppings.append(topping)
return self
def build(self):
return self.pizza
class MargheritaPizzaBuilder(PizzaBuilder):
def __init__(self):
super().__init__()
self.pizza.dough = 'thin'
self.pizza.sauce = 'tomato'
def add_toppings(self):
self.pizza.toppings.extend(['mozzarella', 'basil'])
return self
class PepperoniPizzaBuilder(PizzaBuilder):
def __init__(self):
super().__init__()
self.pizza.dough = 'pan'
self.pizza.sauce = 'tomato'
def add_toppings(self):
self.pizza.toppings.extend(['mozzarella', 'pepperoni'])
return self
class Director:
def __init__(self, builder=None):
self.builder = builder
def set_builder(self, builder):
self.builder = builder
def construct_pizza(self):
if not self.builder:
raise ValueError("Builder is not set")
self.builder.add_topping().build()
在这个例子中,PizzaBuilder类可以创建定制并返回Pizza。MargheritaPizzaBuilder、PepperoniPizzaBuilder则创建特定的Pizza。Director类则可以通过传入不同的PizzaBuilder构建出不同的Pizza。使得同样的构建过程创建出不同的Pizza。