当先锋百科网

首页 1 2 3 4 5 6 7

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)

在上述代码中,我们先创建了两个实例对象dog1dog2,采用不同的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")

在上面的代码中,我们定义了两个类ObserverObservable,以及继承自ObservableDog类和继承自ObserverOwner类。
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 ALeaf 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的基类和两个具体的策略类:ConcreteStrategyAConcreteStrategyB。每个策略类都实现了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。