Python中的工厂方法设计模式

The Factory Method Design Pattern in Python

介绍

在本文中,我们将深入探讨以Python实现的Factory Method Design Pattern。

设计模式定义了针对软件开发中各种反复出现的问题的久经考验的解决方案。 它们不代表实际的代码,而是代表我们可以组织代码以获得最佳结果的方式。

在资源有限的世界中,设计模式可帮助我们以最少的已用资源获得最大的结果。 同样重要的是要注意,设计模式并不适用于所有情况,评估手头的问题以便为特定情况选择最佳方法至关重要。

设计模式分为几大类,尽管主要分为创造模式,结构模式和行为模式。

工厂方法模式是创新设计模式。

工厂方法设计模式

定义

工厂方法在面向对象的编程中用作提供用于创建对象的工厂接口的方法。 这些接口定义通用结构,但不初始化对象。 初始化留给更具体的子类。

父类/接口包含所有可在不同类型的子类之间共享的标准行为和通用行为。 子类又负责根据超类对对象进行定义和实例化。

动机

工厂方法设计模式背后的主要动机是通过创建抽象类来增强代码中的松散耦合,该抽象类将用于创建共享某些公共属性和功能的不同类型的对象。

这将提高灵活性和代码重用性,因为从同一个类继承了共享功能后将不会重写该共享功能。 这种设计模式也称为虚拟构造函数。

库方法通常通过允许客户选择要通过抽象类创建的对象的子类或类型来在库中使用。

工厂方法将接收有关所需对象的信息,将其实例化并返回指定类型的对象。 这使我们的应用程序或库与其他程序或代码段进行单点交互,从而封装了对象创建功能。

工厂方法实施

我们的程序将成为一个用于在创建和其他操作(例如添加颜色和计算形状面积)方面处理形状对象的库。

用户应该能够使用我们的库来创建新对象。 我们可以从创建单个单独的形状开始并按原样使用它们,但这意味着必须为每个可用形状重新编写很多共享逻辑。

解决此重复的第一步是创建一个父形状类,该类具有诸如calculate_area()calculate_perimeter()之类的方法以及诸如尺寸之类的属性。

然后,特定的形状对象将从我们的基类继承。 要创建形状,我们需要确定所需的形状并为其创建子类。

我们将从创建一个抽象类来代表通用形状开始:

1
2
3
4
5
6
7
8
9
import abc
class Shape(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def calculate_area(self):
        pass

    @abc.abstractmethod
    def calculate_perimeter(self):
        pass

这是我们所有形状的基类。 让我们继续创建一些更具体的具体形状:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class Rectangle(Shape):
    def __init__(self, height, width):
        self.height = height
        self.width = width

    def calculate_area(self):
        return self.height * self.width

    def calculate_perimeter(self):
        return 2 * (self.height + self.width)

class Square(Shape):
    def __init__(self, width):
        self.width = width

    def calculate_area(self):
        return self.width ** 2

    def calculate_perimeter(self):
        return 4 * self.width

class Circle(Shape):
    def __init__(self, radius):
        self.radius = radius

    def calculate_area(self):
        return 3.14 * self.radius * self.radius

    def calculate_perimeter(self):
        return 2 * 3.14 * self.radius

到目前为止,我们已经创建了一个抽象类,并将其扩展为适合库中可用的不同形状。 为了创建不同的形状对象,客户将必须知道我们形状的名称和细节并分别执行创建。

这是工厂方法起作用的地方。

Factory Method设计模式将帮助我们从客户端抽象可用的形状,即客户端不必知道所有可用的形状,而只需在运行时创建所需的形状即可。 它还将使我们能够集中和封装对象的创建。

让我们通过创建ShapeFactory来实现此目的,该ShapeFactory将用于基于客户端的输入来创建特定的形状类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class ShapeFactory:
    def create_shape(self, name):
        if name == 'circle':
            radius = input("Enter the radius of the circle:")
            return Circle(float(radius))

        elif name == 'rectangle':
            height = input("Enter the height of the rectangle:")
            width = input("Enter the width of the rectangle:")
            return Rectangle(int(height), int(width))

        elif name == 'square':
            width = input("Enter the width of the square:")
            return Square(int(width))

这是我们的创建界面。 我们不调用具体类的构造函数,而是调用Factory并要求其创建形状。

我们的ShapeFactory通过接收有关形状的信息(例如名称和所需尺寸)来工作。 然后,我们的工厂方法create_shape()将用于创建和返回所需形状的就绪对象。

客户端不必了解有关对象创建或细节的任何信息。 使用工厂对象,他们可以创建关于对象的基本知识:

1
2
3
4
5
6
7
8
9
def shapes_client():
    shape_factory = ShapeFactory()
    shape_name = input("Enter the name of the shape:")

    shape = shape_factory.create_shape(shape_name)

    print(f"The type of object created: {type(shape)}")
    print(f"The area of the {shape_name} is: {shape.calculate_area()}")
    print(f"The perimeter of the {shape_name} is: {shape.calculate_perimeter()}")

运行此代码将导致:

1
2
3
4
5
6
Enter the name of the shape: circle
Enter the radius of the circle: 7

The type of object created: <class '__main__.Circle'>
The area of the circle is: 153.86
The perimeter of the circle is: 43.96

或者,我们可以构建另一种形状:

1
2
3
4
5
6
Enter the name of the shape: square
Enter the width of the square: 5

The type of object created: <class '__main__.Square'>
The area of the square is: 25
The perimeter of the square is: 20

值得注意的是,除了客户端不必对创建过程了解太多之外,当我们想实例化一个对象时,我们不会调用该类的构造函数。 我们要求工厂根据传递给create_shape()函数的信息为我们执行此操作。

利弊

优点

使用Factory Method设计模式的主要优点之一是我们的代码变得松散耦合,因为我们代码的大多数组件都不知道同一代码库的其他组件。

这样就产生了易于理解和测试的代码,并为特定组件添加了更多功能,而不会影响或破坏整个程序。

"工厂方法"设计模式还有助于维护"单一职责原则",其中处理特定功能的类和对象可产生更好的代码。

缺点

创建更多类最终会导致可读性降低。 如果与抽象工厂(工厂的工厂)结合使用,该代码将很快变得冗长,但可维护。

结论

总之,工厂方法设计模式允许我们创建对象而无需指定创建特定对象所需的确切类。 这使我们能够解耦代码并增强其可重用性。

重要的是要注意,就像任何其他设计模式一样,它仅适用于特定情况,而不适用于每个开发方案。 在决定实施"工厂方法设计模式"以充分利用该模式的好处之前,对当前情况进行评估至关重要。