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

行动起来,活在当下

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

目 录CONTENT

文章目录

设计模式 - 生成器模式

Jack.Jia
2023-12-26 / 0 评论 / 0 点赞 / 10 阅读 / 0 字

生成器模式

引言

设计模式是一种在软件工程中解决常见问题的经过验证的解决方案。生成器模式是一种创建型设计模式,它提供了一种在不直接构造对象的情况下创建复杂对象的方法。

生成器模式通常用于创建具有多个可能配置选项的复杂对象。这些对象通常具有大量的构造参数,其中许多参数是可选的。生成器模式使得我们可以逐步构造这样的对象,只设置我们关心的属性,而忽略其他属性。

生成器模式由四个主要部分组成:产品(Product)、生成器(Builder)、具体生成器(ConcreteBuilder)和指挥者(Director)。产品是我们想要构造的复杂对象,生成器定义了创建产品所需的所有步骤,具体生成器实现了这些步骤,而指挥者负责使用生成器来构建产品。

生成器模式结构

生成器模式.png

举例说明

汽车制造

假设我们正在开发一个汽车制造系统,需要生产各种配置不同的汽车。每辆汽车都有很多可能配置选项,如颜色、引擎类型、轮胎大小等。

首先,我们定义一个汽车类来表示产品:

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)” 的出现。

假设我们的构造函数中有十个可选参数, 那么调用该函数会非常不方便; 因此, 我们需要重载这个构造函数, 新建几个只有较少参数的简化版。 但这些构造函数仍需调用主构造函数, 传递一些默认数值来替代省略掉的参数。

当我们希望使用代码创建不同形式的产品 (例如石头或木头房屋) 时, 可使用生成器模式。

如果我们需要创建的各种形式的产品, 它们的制造过程相似且仅有细节上的差异, 此时可使用生成器模式。

基本生成器接口中定义了所有可能的制造步骤, 具体生成器将实现这些步骤来制造特定形式的产品。 同时, 主管类将负责管理制造步骤的顺序。

使用生成器构造组合树或其他复杂对象。

生成器模式让我们能分步骤构造产品。 我们可以延迟执行某些步骤而不会影响最终产品。 我们甚至可以递归调用这些步骤, 这在创建对象树时非常方便。

生成器在执行制造步骤时, 不能对外发布未完成的产品。 这可以避免客户端代码获取到不完整结果对象的情况。

生成器模式优缺点

优点

  • 我们可以分步创建对象, 暂缓创建步骤或递归运行创建步骤。
  • 生成不同形式的产品时, 我们可以复用相同的制造代码。
  • 单一职责原则。 我们可以将复杂构造代码从产品的业务逻辑中分离出来。

缺点

  • 由于该模式需要新增多个类, 因此代码整体复杂程度会有所增加。

与其他模式的关系

总结

生成器模式是一种创建型设计模式,它提供了一种在不直接构造对象的情况下创建复杂对象的方法。它特别适用于那些具有多个可能配置选项的复杂对象。

在汽车制造系统的例子中,生成器模式被用来生产各种配置不同的汽车。每辆汽车都有很多可能配置选项,如颜色、引擎类型、轮胎大小等。通过使用生成器模式,我们可以逐步构造汽车,只设置我们关心的属性,而忽略其他属性。

在文档编辑器的例子中,生成器模式被用来创建多种不同格式的文档。每种文档格式都有自己独特的表示方式,但我们希望能提供一个统一的接口来创建所有类型的文档。通过使用生成器模式,我们可以逐步构造文档,并且可以轻松地添加对新文档格式的支持。

总的来说,生成器模式有助于提高代码的可维护性和可扩展性。它使得我们可以逐步构造复杂对象,只设置我们关心的属性,而忽略其他属性。此外,生成器模式还使得我们可以轻松地添加对新配置选项或新产品类型的支持。

0

评论区