生成器模式
引言
设计模式是一种在软件工程中解决常见问题的经过验证的解决方案。生成器模式是一种创建型设计模式,它提供了一种在不直接构造对象的情况下创建复杂对象的方法。
生成器模式通常用于创建具有多个可能配置选项的复杂对象。这些对象通常具有大量的构造参数,其中许多参数是可选的。生成器模式使得我们可以逐步构造这样的对象,只设置我们关心的属性,而忽略其他属性。
生成器模式由四个主要部分组成:产品(Product)、生成器(Builder)、具体生成器(ConcreteBuilder)和指挥者(Director)。产品是我们想要构造的复杂对象,生成器定义了创建产品所需的所有步骤,具体生成器实现了这些步骤,而指挥者负责使用生成器来构建产品。
生成器模式结构
举例说明
汽车制造
假设我们正在开发一个汽车制造系统,需要生产各种配置不同的汽车。每辆汽车都有很多可能配置选项,如颜色、引擎类型、轮胎大小等。
首先,我们定义一个汽车类来表示产品:
class Car
attr_accessor :color, :engine_type, :tire_size
def initialize
@color = "White"
@engine_type = "Gasoline"
@tire_size = 17
end
end
然后,我们定义一个生成器接口,它定义了创建汽车所需的所有步骤:
module CarBuilder
def set_color(color)
raise NotImplementedError, 'You must implement the set_color method'
end
def set_engine_type(engine_type)
raise NotImplementedError, 'You must implement the set_engine_type method'
end
def set_tire_size(tire_size)
raise NotImplementedError, 'You must implement the set_tire_size method'
end
def get_result
raise NotImplementedError, 'You must implement the get_result method'
end
end
接下来,我们创建一个具体的生成器类来实现这些步骤:
class ConcreteCarBuilder
include CarBuilder
def initialize
reset
end
def reset
@car = Car.new
end
def set_color(color)
@car.color = color
self
end
def set_engine_type(engine_type)
@car.engine_type = engine_type
self
end
def set_tire_size(tire_size)
@car.tire_size = tire_size
self
end
def get_result
car = @car
reset
car
end
end
最后,我们创建一个指挥者类来使用生成器构建汽车:
class Director
attr_accessor :builder
def initialize(builder)
@builder = builder
end
def build_sports_car
@builder.set_color('Red').set_engine_type('Gasoline').set_tire_size(18).get_result
end
def build_family_car
@builder.set_color('Blue').set_engine_type('Diesel').set_tire_size(16).get_result
end
end
现在,我们可以使用指挥者和生成器来创建不同配置的汽车:
builder = ConcreteCarBuilder.new
director = Director.new(builder)
sports_car = director.build_sports_car
family_car = director.build_family_car
这就是生成器模式的基本用法。通过使用生成器模式,我们可以逐步构造复杂对象,只设置我们关心的属性,而忽略其他属性。
文档编辑器
假设我们正在开发一个文档编辑器,需要支持多种不同格式的输出(如 HTML、Markdown、PDF 等)。每种格式都有自己独特的表示方式,但我们希望能提供一个统一的接口来创建所有类型的文档。
首先,我们定义一个文档类来表示产品:
class Document
attr_accessor :title, :text, :author
def initialize(title, text, author)
@title = title
@text = text
@author = author
end
end
然后,我们定义一个生成器接口,它定义了创建文档所需的所有步骤:
module DocumentBuilder
def set_title(title)
raise NotImplementedError, 'You must implement the set_title method'
end
def set_text(text)
raise NotImplementedError
end
def set_author(author)
raise NotImplementedError, 'You must implement the set_author method'
end
def get_result
raise NotImplementedError, 'You must implement the get_result method'
end
end
接下来,我们创建几个具体的生成器类来实现这些步骤:
class HTMLDocumentBuilder
include DocumentBuilder
def initialize
reset
end
def reset
@document = Document.new('', '', '')
end
def set_title(title)
@document.title = "<h1>#{title}</h1>"
self
end
def set_text(text)
@document.text = "<p>#{text}</p>"
self
end
def set_author(author)
@document.author = "<p>Author: #{author}</p>"
self
end
def get_result
document = @document
reset
document
end
end
class MarkdownDocumentBuilder
include DocumentBuilder
def initialize
reset
end
def reset
@document = Document.new('', '', '')
end
def set_title(title)
@document.title = "# #{title}"
self
end
def set_text(text)
@document.text = text
self
end
def set_author(author)
@document.author = "Author: #{author}"
self
end
def get_result
document = @document
reset
document
end
end
最后,我们创建一个指挥者类来使用生成器构建文档:
class Director
attr_accessor :builder
def initialize(builder)
@builder = builder
end
def build_blog_post(title, text, author)
@builder.set_title(title).set_text(text).set_author(author).get_result
end
end
现在,我们可以使用指挥者和生成器来创建不同格式的文档:
html_builder = HTMLDocumentBuilder.new
markdown_builder = MarkdownDocumentBuilder.new
director = Director.new(html_builder)
html_document = director.build_blog_post('My Title', 'My Text', 'My Author')
director.builder = markdown_builder
markdown_document = director.build_blog_post('My Title', 'My Text', 'My Author')
这就是生成器模式在文档编辑器中的应用。通过使用生成器模式,我们可以逐步构造复杂的文档对象,并且可以轻松地添加对新文档格式的支持。
生成器模式适合应用场景
使用生成器模式可避免 “重叠构造函数 (telescoping constructor)” 的出现。
假设我们的构造函数中有十个可选参数, 那么调用该函数会非常不方便; 因此, 我们需要重载这个构造函数, 新建几个只有较少参数的简化版。 但这些构造函数仍需调用主构造函数, 传递一些默认数值来替代省略掉的参数。
当我们希望使用代码创建不同形式的产品 (例如石头或木头房屋) 时, 可使用生成器模式。
如果我们需要创建的各种形式的产品, 它们的制造过程相似且仅有细节上的差异, 此时可使用生成器模式。
基本生成器接口中定义了所有可能的制造步骤, 具体生成器将实现这些步骤来制造特定形式的产品。 同时, 主管类将负责管理制造步骤的顺序。
使用生成器构造组合树或其他复杂对象。
生成器模式让我们能分步骤构造产品。 我们可以延迟执行某些步骤而不会影响最终产品。 我们甚至可以递归调用这些步骤, 这在创建对象树时非常方便。
生成器在执行制造步骤时, 不能对外发布未完成的产品。 这可以避免客户端代码获取到不完整结果对象的情况。
生成器模式优缺点
优点
- 我们可以分步创建对象, 暂缓创建步骤或递归运行创建步骤。
- 生成不同形式的产品时, 我们可以复用相同的制造代码。
- 单一职责原则。 我们可以将复杂构造代码从产品的业务逻辑中分离出来。
缺点
- 由于该模式需要新增多个类, 因此代码整体复杂程度会有所增加。
与其他模式的关系
- 在许多设计工作的初期都会使用工厂方法模式 (较为简单, 而且可以更方便地通过子类进行定制), 随后演化为使用抽象工厂模式、 原型模式或生成器模式 (更灵活但更加复杂)。
- 生成器重点关注如何分步生成复杂对象。 抽象工厂专门用于生产一系列相关对象。 抽象工厂会马上返回产品, 生成器则允许我们在获取产品前执行一些额外构造步骤。
- 我们可以在创建复杂组合模式树时使用生成器, 因为这可使其构造步骤以递归的方式运行。
- 我们可以结合使用生成器和桥接模式: 主管类负责抽象工作, 各种不同的生成器负责实现工作。
- 抽象工厂、 生成器和原型都可以用单例模式来实现。
总结
生成器模式是一种创建型设计模式,它提供了一种在不直接构造对象的情况下创建复杂对象的方法。它特别适用于那些具有多个可能配置选项的复杂对象。
在汽车制造系统的例子中,生成器模式被用来生产各种配置不同的汽车。每辆汽车都有很多可能配置选项,如颜色、引擎类型、轮胎大小等。通过使用生成器模式,我们可以逐步构造汽车,只设置我们关心的属性,而忽略其他属性。
在文档编辑器的例子中,生成器模式被用来创建多种不同格式的文档。每种文档格式都有自己独特的表示方式,但我们希望能提供一个统一的接口来创建所有类型的文档。通过使用生成器模式,我们可以逐步构造文档,并且可以轻松地添加对新文档格式的支持。
总的来说,生成器模式有助于提高代码的可维护性和可扩展性。它使得我们可以逐步构造复杂对象,只设置我们关心的属性,而忽略其他属性。此外,生成器模式还使得我们可以轻松地添加对新配置选项或新产品类型的支持。
评论区