1:主题拆解
①基本介绍
②生产法拉利场景分析
③基于四大角色拆解
④建造者模式优缺点
⑤使用场景
2:基本介绍
建造者模式就是将一个个简单的对象一步一步构建成一个复杂的对象。
建造者模式将一个复杂对象的构建与表示分离,使得同样的构建过程可以创建不同的表示。
我们生活中有很多可以用建造者模式来解释。
譬如在生产汽车的流水线作业中,我们需要先将生产汽车所需的一个一个的内部构建建造出来,例如发动机,车门,车轮,方向盘,水箱等。
对于我们用户来说,我们并不需要知道这个汽车是怎么建造出来的,各个部件是怎么组装的,我们只需要知道这是一辆汽车,对于销售人员只需要知道客户需要什么样的汽车,他告诉建造者模式中的指挥者去生产对应的车就可以了。
像这样,建造者返回给客户一个完整的的产品对象,而客户端无须关心该对象所包含的额属性和组建方式,这就是建造者模式的设计动机。
3:生产法拉利场景分析
我们以生产法拉利场景进行分析,例如生产一辆法拉利需要 依次生产引擎,发动机,车灯,最后再进行组装。
基础版
①依次定义引擎,发动机,车灯以及车的类
public class Engine
{
public string Name { get; set; }
}
public class Wheels
{
public string Name { get; set; }
}
public class Light
{
public string Name { get; set; }
}
public class Car
{
public Car(Engine engine, Light light, Wheels wheels)
{ }
public string Name { get; set; }
}
②法拉利工人类
public class BuilderFerrari
{
private Engine _Engine = null;
private Wheels _Wheels = null;
private Light _Light = null;
public void Engine()
{
this._Engine = new Engine()
{
Name = "_Engine"
};
Console.WriteLine("{0} build Engine", this.GetType().Name);
}
public void Wheels()
{
this._Wheels = new Wheels()
{
Name = "_Wheels"
};
Console.WriteLine("{0} build Wheels", this.GetType().Name);
}
public void Light()
{
this._Light = new Light()
{
Name = "_Light"
};
Console.WriteLine("{0} build Light", this.GetType().Name);
}
public Car Car()
{
Console.WriteLine("组装 {0} {1} {2}", this._Engine, this._Light, this._Wheels);
Console.WriteLine("{0} build Mondeo", this.GetType().Name);
return new Car(this._Engine, this._Light, this._Wheels)
{
Name = "Ferrari 955"
};
}
}
③上端执行开始生产汽车
BuilderFerrari builder = new BuilderFerrari();
builder.Engine();
builder.Wheels();
builder.Light();
builder.Car();
分析:上端的调用建议是面向抽象,而不是面向细节,因此需要进行升级抽象。
升级版
基于抽象工厂的方式,对产品簇进行封装
①添加工人抽象工厂类
public abstract class AbstractBuilder
{
public abstract void Engine();
public abstract void Wheels();
public abstract void Light();
public abstract Car Car();
}
②工人实现类进行继承,并且重写实现抽象类中的方法
public class BuilderFerrari : AbstractBuilder
{
private Engine _Engine = null;
private Wheels _Wheels = null;
private Light _Light = null;
public override void Engine()
{
this._Engine = new Engine()
{
Name = "_Engine"
};
Console.WriteLine("{0} build Engine", this.GetType().Name);
}
public override void Wheels()
{
this._Wheels = new Wheels()
{
Name = "_Wheels"
};
Console.WriteLine("{0} build Wheels", this.GetType().Name);
}
public override void Light()
{
this._Light = new Light()
{
Name = "_Light"
};
Console.WriteLine("{0} build Light", this.GetType().Name);
}
public override Car Car()
{
Console.WriteLine("组装 {0} {1} {2}", this._Engine, this._Light, this._Wheels);
Console.WriteLine("{0} build Mondeo", this.GetType().Name);
return new Car(this._Engine, this._Light, this._Wheels)
{
Name = "Ferrari 955"
};
}
}
③上端执行开始生产汽车
AbstractBuilder builder = new BuilderFerrari();
builder.Engine();
builder.Wheels();
builder.Light();
builder.Car();
分析:从上端的调用我们可以发现,其实上端只需要一辆车即可,至于生产车的过程上端完全不用知道,也必要知道。继续这一点,我们基于进行升级
终极版
①添加组装类
public class Director
{
private AbstractBuilder _AbstractBuilder = null;
public Director(AbstractBuilder builder)
{
this._AbstractBuilder = builder;
}
public Car GetCar()
{
this._AbstractBuilder.Engine();
this._AbstractBuilder.Wheels();
this._AbstractBuilder.Light();
return this._AbstractBuilder.Car();
}
}
②上端的调用调整
AbstractBuilder builder = new BuilderFerrari();
Director director = new Director(builder);
director.GetCar();
分析:此时从上端的调用都是基于抽象在编程,生产车的过程全部进行了封装
产品族扩展
如果现在需要生产一辆保时捷,在当前终结版的基础上需要做哪些调整呢?
①添加保时捷的工人类,继承自抽象工厂类,此时的操作与抽象工厂相同
public class BuilderPorsche : AbstractBuilder
{
private Engine _Engine = null;
private Wheels _Wheels = null;
private Light _Light = null;
public override void Engine()
{
this._Engine = new Engine()
{
Name = "_Engine"
};
Console.WriteLine("{0} build Engine", this.GetType().Name);
}
public override void Wheels()
{
this._Wheels = new Wheels()
{
Name = "_Wheels"
};
Console.WriteLine("{0} build Wheels", this.GetType().Name);
}
public override void Light()
{
this._Light = new Light()
{
Name = "_Light"
};
Console.WriteLine("{0} build Light", this.GetType().Name);
}
public override Car Car()
{
Console.WriteLine("组装 {0} {1} {2}", this._Engine, this._Light, this._Wheels);
Console.WriteLine("{0} build 保时捷 915", this.GetType().Name);
return new Car(this._Engine, this._Light, this._Wheels)
{
Name = "保时捷 915"
};
}
}
②上端直接调用,生产一辆保时捷
AbstractBuilder builder = new BuilderPorsche();
Director director = new Director(builder);
director.GetCar();
分析:从实现的过程中我们发现,增加新的产品族,遵循开闭原则,只用添加新的功能,即可此处与抽象方法模式相同。在此基础上我们对复制的生产流程进行封装,即就是本节的主题:建造者模式。
4:基于四大角色拆解
1:抽象建造者(Builder)
创建一个产品对象的各个部件指定的抽象类,例如本实例中的AbstractBuilder类
2:具体建造者(ConcreteBuilder)
实现抽象建造者接口,构建和装配各个部件。例如本实例中的BuilderFerrari类
3:指挥者(Director)
它负责安排复杂对象的建造次序,指挥者与抽象建造者之间存在关联关系,主要有两个用途
①隔离了客户与对象的生成过程
②负责控制产品对象的生产过程
例如本实例中的Director类
4:产品(Product)
产品角色,一个具体的产品对象。
例如本实例中的法拉利955
5:建造者模式优缺点
1:优点
①封装细节:建造者模式中客户端不需要知道产品内部组成细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象。
②扩展性好:每一个具体建造者都相对独立,而与其他建造者无关,伊尼茨可以很方便地替换具体建造者或增加新的具体建造者,用户使用不同的具体建造者即可得到不同的产品对象。由于指挥者针对抽象建造者编程,增加新的具体建造者无须修改原有类库的代码,扩展方便,符合开闭原则。
③控制创建过程:将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰,更加精细地控制创建过程。
2:缺点
①范围受限:建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,例如很多组成部分不相同,就不适合使用建造者模式,因此使用范围受到一定限制。
②建造者多:如果产品内部结构复杂多变,可能会需要定义很多具体建造者类来实现这种变化,增大系统的理解难度与运行成本。
6:使用场景
需要生成的产品对象有复杂的内部结构,这些产品对象通常包含多个成员变量。
需要生成的产品对象的属性相互依赖,需要指定其生成顺序。
对象的创建过程独立于创建该对象的类。在建造者模式中通过引入指挥者类,将创建过程封装在指挥者类中,而不再建造者类或者客户类中。
隔离复杂对象的创建与使用,并使得相同的创建过程可以创建不同的产品。