建造者模式
本篇博客将介绍建造者模式,建造者模式是一种比较复杂的创建型设计模式,建造者模式相比较于工厂模式,建造者模式更加关注于一个对象的组装过程,通常建造者模式适用于复杂对象的创建,且常常会和工厂模式联动使用。
模式分类
创建型设计模式。
模式产生的原因
在实际的软件开发中会有一种复杂的对象需求,复杂对象一般指的是利用组合复用原则将若干个子对象组合成的对象,子对象之间可能还存在一定的次序。这时我们如果想要创建这个对象就需要逐个创建它的子对象。如果你的抽象工厂理解得不错的话,你会发现这里的需求是可以通过抽象工厂来解决的,只要我们将一个产品族换成复杂对象的子对象,并在客户端和工厂之间添加一个指挥者类,这就基本构成了我们今天要说的创建者模式。
模式灵感的来源
比如现实世界中的车辆生产,一辆汽车并不是直接生产出来的,我们需要先生产它的四个车轮,方向盘,发动机等等,最后通过工厂的流水线将他组装起来。建造者模式同理,建造者模式将子对象的组装和他的表示分离,关注如何一步步创建出一个复杂的拥有多个子对象的对象。就如同现实生活中的车辆流水线。
模式类图
由图可知建造者模式由4个角色构成:
Builder(抽象建造者):
为创建一个产品对象的各个部件指定抽象接口,在该接口或者类中一般提供两种方法,第一种就是各个组件的创建方法,另一类方法是对象返回方法,用于将构建完成的对象返回。
ConcreteBuilder(具体建造者):
具体建造者实现或者继承抽象建造者,实现各个组件的创建方法和对象返回方法。
Product(产品):
被构建的复杂对象,包含多个组件。
Director(指挥者类):
指挥者负责安排复杂对象的建造次序,指挥者和建造者之间存在关联关系,可以在其Construct方法中调用建造者建造组件的方法完成建造任务并返回建造完成的对象,这里我们需要先指定使用哪个建造者。一般情况下用户只需要和指挥者类交互即可。
经典代码实现
这里模拟游戏人物创建过程:
Builder:
namespace Builder.Builder.Example
{
public interface IActorBuilder
{
Actor Product { get; set; }
void CreateName();
void CreateAge();
void CreateSex();
Actor ReturnResult();
}
}
ConcreteBuilder:
namespace Builder.Builder.Example
{
public class AngleBuilder : IActorBuilder
{
private Actor _product;
public Actor Product
{
get
{
if (_product == null)
{
_product = new Actor();
}
return _product;
}
set => _product = value;
}
public void CreateName()
{
Product.Name = "天使";
}
public void CreateAge()
{
Product.Age = 20;
}
public void CreateSex()
{
Product.Sex = "女";
}
public Actor ReturnResult()
{
return Product;
}
}
}
Product:
namespace Builder.Builder.Example
{
public class Actor
{
public string Name { get; set; }
public int Age { get; set; }
public string Sex { get; set; }
}
}
Director:
namespace Builder.Builder.Example
{
public class ActorDirector
{
private IActorBuilder _builder;
public void SetBuilder(IActorBuilder builder)
{
_builder = builder;
}
public Actor Construct()
{
_builder.CreateAge();
_builder.CreateName();
_builder.CreateSex();
return _builder.ReturnResult();
}
}
}
Program:
using System;
using Builder.Builder.Example;
namespace Builder
{
internal class Program
{
public static void Main(string[] args)
{
ActorDirector ad = new ActorDirector();
ad.SetBuilder(new AngleBuilder());
Actor actor = ad.Construct();
Console.WriteLine($"名称为:{actor.Name}");
Console.WriteLine($"年龄为:{actor.Age}");
Console.WriteLine($"性别为:{actor.Sex}");
}
}
}
建造者模式的优化
Director省略
指挥者类Director是建造者模式的重要组成部分,其负责指挥建造者建造各个组件的顺序并向用户返回完整的产品对象,有时为了简化系统,我们可以省略Director的书写,将Director相关代码也就是Construct方法融入Builder中。这样用户直接调用该建造者的相关方法即可。
钩子方法引入
钩子方法的引入是为了精确控制组件的生产过程。什么是钩子方法?钩子方法通常是一个bool返回值的方法,声明在建造者类中,使用在指挥者类中,用于判断是否构建一个组件。
建造者修改:
namespace Builder.Builder.Example
{
public class AngleBuilder : IActorBuilder
{
//...之前的代码
//假设该角色没有名字,此方法在父类中声明为虚方法,因为不一定会用到
public override bool HasName()
{
return false;
}
}
}
指挥者修改:
为了方便观察这里将不重要的代码注释掉了。
namespace Builder.Builder.Example
{
public class ActorDirector
{
/*private IActorBuilder _builder;
public void SetBuilder(IActorBuilder builder)
{
_builder = builder;
}*/
public Actor Construct()
{
//_builder.CreateAge();
if(!_builder.HasName())
_builder.CreateName();
//_builder.CreateSex();
return _builder.ReturnResult();
}
}
}
建造者模式的总结
建造者模式的优点:
- 在建造者模式中用户不需要知道产品内部组成的细节,将产品本身和产品的创建过程解耦,使得相同的创建过程可以创建不同的对象。
- 每一个具体的建造者都是相互独立的,所以我们可以很方便的添加新建造者,用户则可以使用不同的建造者来获得不同的产品对象。
- 用户可以更加精确的控制产品的创建过程。
建造者模式的缺点:
- 建造者使用的前提是各个产品之间有着极高的相似性,如果差异性过大就不适合使用建造者模式。不过如果产品存在产品族,那么这个问题可以通过工厂模式和建造者模式的组合来解决。
,将产品本身和产品的创建过程解耦,使得相同的创建过程可以创建不同的对象。
2. 每一个具体的建造者都是相互独立的,所以我们可以很方便的添加新建造者,用户则可以使用不同的建造者来获得不同的产品对象。
3. 用户可以更加精确的控制产品的创建过程。