Skip to content

Commit

Permalink
docs(design-patterns): add factory method
Browse files Browse the repository at this point in the history
  • Loading branch information
zyf722 committed Apr 15, 2024
1 parent 263de75 commit 4232d46
Showing 1 changed file with 139 additions and 0 deletions.
139 changes: 139 additions & 0 deletions docs/design-patterns/creational/factory-method.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
# 工厂方法模式
**工厂方法**在一个特定的创建者(Creator)父类中提供了一个创建对象的接口,但是允许子类决定实例化的类。这种模式的目的是将实例化的过程推迟到子类中,以便在提供相同接口的情况下,允许子类选择实例化的类。

工厂方法将**创建产品的代码****实际使用产品的代码**分离。

## 结构

![工厂方法示意图](https://refactoringguru.cn/images/patterns/diagrams/factory-method/structure.png)

工厂方法通常包括以下二者:
- **产品**(Product):产品是工厂方法创建的对象的接口或抽象基类。
- **创建者**(Creator):创建者是一个类,其中包含一个工厂方法,该方法返回一个产品对象。创建者的子类可以实现工厂方法以更改产品的类型。

而二者又拥有相应的实现,分别代表具体产品和具体的创建者。

## 应用场景

- 当构建一个类的对象却不知道对象的实际类型时
- 当创建对象时需要特定的逻辑,而该逻辑需要多态性时
- 当产品可能出现继承关系,而创建者希望通过同一接口创建所有产品时
- 例如 UI 库(创建者)和按钮(产品)之间的关系

## 优缺点
### 优点
- 避免创建者和具体产品之间的紧密耦合。
- **单一职责原则**。创建产品的逻辑被分离到一个单独的类中,这使得代码更容易维护。
- **开闭原则**。对扩展开放,对修改关闭。可以在不更改现有代码的情况下引入新的产品类型。

### 缺点
- 可能会导致代码中的类数量激增,因为每个产品都需要一个具体的创建者类。这可能会使代码更难理解。

## 代码示例

下面的示例展示了一个简单的有关门的工厂方法。门有两种类型:铁门和木门。`DoorFactory` 是一个创建者,它有一个工厂方法 `make_door`,该方法返回一个门对象。`IronDoorFactory``WoodenDoorFactory` 是具体的创建者,它们分别返回铁门和木门。

用户可以通过调用 `deliver_door` 方法来获得一个门对象(无论是哪个工厂),并且可以使用该对象的继承方法 `area` 和多态方法 `open`

```python
# [x] Pattern: Factory method
# A class that delegates the creation of objects to its subclasses,
# allowing each subclass to decide which object to create.

# Why we use it
# When constructing a class that does not know the type of the objects it will create
# In other words, we use a universal method interface to create objects, but the specific implementation is left to the subclasses

from abc import ABC, abstractmethod
from dataclasses import dataclass


@dataclass
class Door(ABC):
width: int
height: int

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

@abstractmethod
def _cut_material(self):
raise NotImplementedError

@abstractmethod
def _install_components(self):
raise NotImplementedError

@abstractmethod
def open(self):
raise NotImplementedError


@dataclass
class IronDoor(Door):
def _cut_material(self):
print("Cutting iron sheets")

def _install_components(self):
print("Installing iron lock")

def open(self):
print("Opening iron door")


@dataclass
class WoodenDoor(Door):
def _cut_material(self):
print("Cutting raw timber")

def _install_components(self):
print("Installing wooden handle")

def open(self):
print("Opening wooden door")


class DoorFactory(ABC):
@abstractmethod
def make_door(self, width: int, height: int) -> Door:
raise NotImplementedError

def deliver_door(self, width: int, height: int) -> Door:
print("Manufacturing door")
door = self.make_door(width, height)
print("Testing door")
door.open()
return door


class IronDoorFactory(DoorFactory):
def make_door(self, width: int, height: int) -> IronDoor:
door = IronDoor(width, height)
door._cut_material()
door._install_components()
return door


class WoodenDoorFactory(DoorFactory):
def make_door(self, width: int, height: int) -> WoodenDoor:
door = WoodenDoor(width, height)
door._cut_material()
door._install_components()
return door


def main():
iron_factory = IronDoorFactory()
iron_door = iron_factory.deliver_door(100, 200)
print(f"Now we have an iron door with area {iron_door.area()}\n")
iron_door.open()

wooden_factory = WoodenDoorFactory()
wooden_door = wooden_factory.deliver_door(100, 200)
print(f"Now we have a wooden door with area {wooden_door.area()}")
wooden_door.open()


if __name__ == "__main__":
main()
```

0 comments on commit 4232d46

Please sign in to comment.