侧边栏壁纸
博主头像
极客日记 博主等级

行动起来,活在当下

  • 累计撰写 93 篇文章
  • 累计创建 17 个标签
  • 累计收到 1 条评论

目 录CONTENT

文章目录

设计模式 - 工厂方法模式

Jack.Jia
2023-12-09 / 0 评论 / 0 点赞 / 8 阅读 / 0 字

工厂方法模式

引言

设计模式是软件工程中的一种常见实践,它们提供了解决软件设计中常见问题的通用解决方案。其中一个最常用且实用的设计模式是工厂方法模式。

工厂方法模式是一种创建型设计模式,它提供了一种封装对象创建过程的方式。在工厂方法模式中,我们不直接使用 new 关键字来创建对象,而是通过一个工厂方法来创建对象。这样,我们可以在不改变代码的情况下更改所创建对象的类型。

工厂方法模式结构

工厂方法.png

举例说明

图形界面组件

假设我们正在开发一个跨平台的客户端软件,需要支持多种操作系统(如 Windows、MacOS、Linux 等)。每种操作系统都有自己独特的方式来创建和管理软件的窗口和控件,但我们希望能提供一个统一的接口来创建所有类型的组件。

首先,我们定义一个 Component 接口,它定义了所有组件必须实现的方法:

module Component
  def draw
    raise NotImplementedError, 'You must implement the draw method'
  end
end

然后,我们为每种支持的操作系统创建一个具体类。这些类实现了 Component 接口:

class WindowsButton
  include Component

  def draw
    # 使用 Windows API 来绘制按钮...
  end
end

class MacOSButton
  include Component

  def draw
    # 使用 MacOS API 来绘制按钮...
  end
end

class LinuxButton
  include Component

  def draw
    # 使用 Linux API 来绘制按钮...
  end
end
接下来,我们创建一个 `ComponentFactory` 类,它提供了一个工厂方法来创建不同类型的组件:

class ComponentFactory
def self.create_component(type)

case type
when :windows
  WindowsButton.new
when :macos
  MacOSButton.new
when :linux
  LinuxButton.new
else
  raise "Invalid component type: #{type}"
end

end
end

现在,无论我们需要哪种类型的组件,都可以使用相同的代码来创建:

button = ComponentFactory.create_component(:windows)
button.draw

### 数据库连接

假设我们正在开发一个应用,需要支持多种数据库(如MySQL、PostgreSQL、SQLite等)。每种数据库都有自己独特的连接方式和参数,但我们希望能提供一个统一的接口来创建所有类型的数据库连接。

首先,我们定义一个 `DatabaseConnection` 接口,它定义了所有数据库连接必须实现的方法:

module DatabaseConnection
def connect(params)

raise NotImplementedError, 'You must implement the connect method'

end

def disconnect

raise NotImplementedError, 'You must implement the disconnect method'

end
end

然后,我们为每种支持的数据库创建一个具体类。这些类实现了 `DatabaseConnection` 接口:

class MySQLConnection
include DatabaseConnection

def connect(params)

# 使用 MySQL API 来建立连接...

end

def disconnect

# 断开 MySQL 连接...

end
end

class PostgreSQLConnection
include DatabaseConnection

def connect(params)

# 使用 PostgreSQL API 来建立连接...

end

def disconnect

# 断开 PostgreSQL 连接...

end
end

class SQLiteConnection
include DatabaseConnection

def connect(params)

# 使用 SQLite API 来建立连接...

end

def disconnect

# 断开 SQLite 连接...

end
end

接下来,我们创建一个 `DatabaseConnectionFactory` 类,它提供了一个工厂方法来创建不同类型的数据库连接:

class DatabaseConnectionFactory
def self.create_connection(type)

case type
when :mysql
  MySQLConnection.new
when :postgresql
  PostgreSQLConnection.new
when :sqlite
  SQLiteConnection.new
else
  raise "Invalid database type: #{type}"
end

end
end

现在,无论我们需要哪种类型的数据库连接,都可以使用相同的代码来创建并使用:

connection = DatabaseConnectionFactory.create_connection(:mysql)
connection.connect({host: 'localhost', user: 'root', password: 'password'})

Do some database operations…

connection.disconnect()

### 支付系统

假设我们正在开发一个电商应用,需要支持多种支付方式(如信用卡支付、PayPal支付、比特币支付等)。每种支付方式都有自己独特的实现方式,但我们希望能提供一个统一的接口来创建所有类型的支付对象。

首先,我们定义一个 Payment 接口,它定义了所有支付对象必须实现的方法:

module Payment
def pay(amount)

raise NotImplementedError, 'You must implement the pay method'

end
end

然后,我们为每种支持的支付方式创建一个具体类。这些类实现了 `Payment` 接口:

class CreditCardPayment
include Payment

def pay(amount)

# 使用信用卡支付 API 来处理支付...
puts "Paid #{amount} using Credit Card"

end
end

class AliPayPayment
include Payment

def pay(amount)

# 使用 AliPay API 来处理支付...
puts "Paid #{amount} using AliPay"

end
end

class BitcoinPayment
include Payment

def pay(amount)

# 使用比特币支付 API 来处理支付...
puts "Paid #{amount} using Bitcoin"

end
end

接下来,我们创建一个 `PaymentFactory` 类,它提供了一个工厂方法来创建不同类型的支付对象:

class PaymentFactory
def self.create_payment(type)

case type
when :credit_card
  CreditCardPayment.new
when :alipay
  AliPayPayment.new
when :bitcoin
  BitcoinPayment.new
else
  raise "Invalid payment type: #{type}"
end

end
end

现在,无论我们需要哪种类型的支付方式,都可以使用相同的代码来创建并使用:

payment = PaymentFactory.create_payment(:credit_card)
payment.pay(100.00)

## 工厂方法模式适合应用场景

 ### 当我们在编写代码的过程中, 如果无法预知对象确切类别及其依赖关系时, 可使用工厂方法。

工厂方法将创建产品的代码与实际使用产品的代码分离, 从而能在不影响其他代码的情况下扩展产品创建部分代码。

例如, 如果需要向应用中添加一种新产品, 我们只需要开发新的创建者子类, 然后重写其工厂方法即可。

### 如果我们希望用户能扩展我们软件库或框架的内部组件, 可使用工厂方法。

继承可能是扩展软件库或框架默认行为的最简单方法。 但是当我们使用子类替代标准组件时, 框架如何辨识出该子类?

解决方案是将各框架中构造组件的代码集中到单个工厂方法中, 并在继承该组件之外允许任何人对该方法进行重写。

让我们看看具体是如何实现的。 假设我们使用开源 UI 框架编写自己的应用。 我们希望在应用中使用圆形按钮, 但是原框架仅支持矩形按钮。 我们可以使用 `圆形按钮`Round­Button子类来继承标准的 `按钮`Button类。 但是, 我们需要告诉 `UI框架`UIFramework类使用新的子类按钮代替默认按钮。

为了实现这个功能, 我们可以根据基础框架类开发子类 `圆形按钮 UI`UIWith­Round­Buttons , 并且重写其 `create­Button`创建按钮方法。 基类中的该方法返回 `按钮`对象, 而我们开发的子类返回 `圆形按钮`对象。 现在, 我们就可以使用 `圆形按钮 UI`类代替 `UI框架`类。 就是这么简单!

### 如果我们希望复用现有对象来节省系统资源, 而不是每次都重新创建对象, 可使用工厂方法。

 在处理大型资源密集型对象 (比如数据库连接、 文件系统和网络资源) 时, 我们会经常碰到这种资源需求。

让我们思考复用现有对象的方法:

1. 首先, 我们需要创建存储空间来存放所有已经创建的对象。
2. 当他人请求一个对象时, 程序将在对象池中搜索可用对象。
3. … 然后将其返回给客户端代码。
4. 如果没有可用对象, 程序则创建一个新对象 (并将其添加到对象池中)。

这些代码可不少! 而且它们必须位于同一处, 这样才能确保重复代码不会污染程序。

可能最显而易见, 也是最方便的方式, 就是将这些代码放置在我们试图重用的对象类的构造函数中。 但是从定义上来讲, 构造函数始终返回的是**新对象**, 其无法返回现有实例。

因此, 我们需要有一个既能够创建新对象, 又可以重用现有对象的普通方法。 这听上去和工厂方法非常相像。

## 工厂方法模式优缺点

### 优点

- 我们可以避免创建者和具体产品之间的紧密耦合。
-  *单一职责原则*。 我们可以将产品创建代码放在程序的单一位置, 从而使得代码更容易维护。
-  *开闭原则*。 无需更改现有客户端代码, 我们就可以在程序中引入新的产品类型。

### 缺点

-  应用工厂方法模式需要引入许多新的子类, 代码可能会因此变得更复杂。 最好的情况是将该模式引入创建者类的现有层次结构中。

## 与其他模式的关系

- 在许多设计工作的初期都会使用[工厂方法模式](https://refactoringguru.cn/design-patterns/factory-method) (较为简单, 而且可以更方便地通过子类进行定制), 随后演化为使用[抽象工厂模式](https://refactoringguru.cn/design-patterns/abstract-factory)、 [原型模式](https://refactoringguru.cn/design-patterns/prototype)或[生成器模式](https://refactoringguru.cn/design-patterns/builder) (更灵活但更加复杂)。
- [抽象工厂模式](https://refactoringguru.cn/design-patterns/abstract-factory)通常基于一组[工厂方法](https://refactoringguru.cn/design-patterns/factory-method), 但我们也可以使用[原型模式](https://refactoringguru.cn/design-patterns/prototype)来生成这些类的方法。
- 我们可以同时使用[工厂方法](https://refactoringguru.cn/design-patterns/factory-method)和[迭代器模式](https://refactoringguru.cn/design-patterns/iterator)来让子类集合返回不同类型的迭代器, 并使得迭代器与集合相匹配。
- [原型](https://refactoringguru.cn/design-patterns/prototype)并不基于继承, 因此没有继承的缺点。 另一方面, *原型*需要对被复制对象进行复杂的初始化。 [工厂方法](https://refactoringguru.cn/design-patterns/factory-method)基于继承, 但是它不需要初始化步骤。
- [工厂方法](https://refactoringguru.cn/design-patterns/factory-method)是[模板方法模式](https://refactoringguru.cn/design-patterns/template-method)的一种特殊形式。 同时, *工厂方法*可以作为一个大型*模板方法*中的一个步骤。

## 总结

工厂方法模式是一种实用的设计模式,它封装了对象创建过程,增强了代码的可维护性和扩展性。在图形界面组件、数据库连接和支付系统的应用中,我们看到了其灵活性:可以轻松地切换创建的对象类型,而无需修改代码。这种模式遵循了开闭原则,使得代码对扩展开放,对修改关闭。
0

评论区