“对象创建” 模式
- 通过 “对象创建” 模式绕开 new,来避免 new 导致的紧耦合(依赖具体类),从而支持对象创建的稳定。它是接口抽象之后的第一步工作。
- 典型模式:
- 简单工厂模式
- Factory Method
- Abstract Factory
- Propotype
- Builder
一、代码示例
**背景:**假设我们要用程序画一个小人,要求小人要有头、身体、两手、两脚
Pen p = new Pen(Color.Yellow);
Graphics gThin = pictureBox1.CreateGraphics();
gThin.DrawEllipse(p, 50, 20, 30, 30); //头
gThin.DrawLine(p, 60, 50, 10, 50); //身体
gThin.DrawLine(p, 60, 50, 40, 100); //左手
gThin.DrawLine(p, 70, 50, 90, 100); //右手
gThin.DrawLine(p, 60, 100, 45, 150); //左脚
gThin.DrawLine(p, 70, 1000, 85, 150); //右脚
这样,我们就画出来一个小人了
**需求变更:**换一个身体比较胖的小人,此时我们直接按照原来的流程进行编码
Pen p = new Pen(Color.Yellow);
Graphics gFat = pictureBox1.CreateGraphics();
gFat.DrawEllipse(p, 50, 20, 30, 30); //头
gFat.DrawLine(p, 40, 50, 40, 50); //身体
gFat.DrawLine(p, 50, 50, 30, 100); //左手
gFat.DrawLine(p, 80, 50, 100, 100); //右手
gFat.DrawLine(p, 60, 100, 45, 150); //左脚
这时我们突然发现因为粗心少画了一条腿。
像这种因为粗心而导致程序异常在平时开发中还是比较常见的,比如我们在程序里需要画100个不同的小人,在高矮胖瘦上有细微的差别,我们在每次创建小人时都用上述的代码,这样是很容易有遗漏。
所以我们需要将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示
想要实现这种效果,我们需要定义一个通用的构造类 PersonBuilder,在其中把创建头、身体、左手、右手、左脚、右脚的方法都定义成抽象方法
abstract class PersonBuilder {
protected Graphics;
protected Pen p;
public PersonBuilder(Graphics g, Pen p) {
this.g = g;
this.p = p;
}
public abstract void buildHead();
public abstract void buildBody();
public abstract void buildArmLeft();
public abstract void buildArmRight();
public abstract void buildLegLeft();
public abstract void buildLegRight();
}
然后,当我们想要建造一个胖的小人时,就需要让胖人类去继承这个抽象类,那就必须重写这些抽象方法,否则编译都无法通过。
class PersonThinBuider extends PersonBuilder {
public PesonThinBuilder(Graphics g, Pen p) {
super(g, p);
}
@Override
public void buildHead() {
g.DrawEllipse(p, 50, 20, 30, 30); //头
}
@Override
public void buildBody() {
g.DrawLine(p, 40, 50, 40, 50); //身体
}
@Override
public void buildArmLeft() {
g.DrawLine(p, 50, 50, 30, 100); //左手
}
@Override
public void buildArmRight() {
g.DrawLine(p, 80, 50, 100, 100); //右手
}
@Override
public void buildLegLeft() {
g.DrawLine(p, 60, 100, 45, 150); //左脚
}
@Override
public void buildLegRight() {
g.DrawLine(p, 70, 100, 45, 150); //右脚
}
}
此时,我们就把创建小人的流程全部定下来了,但是,对于客户端来说,他还是需要指导头身手脚这些方法才能调用,问题还是没有得到最终的解决。这时我们还需要一个指挥者类
class PersonDirector {
private PersonBuilder pb;
public PersonDirector(PersonBuilder pb) {
this.pb = pb;
}
public void createPerson() {
pb.buildHead();
pb.buildBody();
pb.buildArmLeft();
pb.buildArmRight();
pb.buildLegLeft();
pb.buildLegRight();
}
}
PersonDirector类就可以根据用户的选择来建造小人了,而在建造的过程在指挥者这里完成了,用户就不需要知道了,而且,由于这个过程每一步都是一定要做的,那就不会少让少画一只手、少画一条腿的问题出现了
代码结构图如下:
客户端调用代码:
Pen p = new Pen(Color.Yellow);
PersonThinBuilder ptb = new PersonThinBuilder(pictureBox1.createGraphics(), p);
PersonDirector pdThin = new PersonDirector(ptb);
pdThin.createPerson();
PersonThinBuilder pfb = new PersonThinBuilder(pictureBox2.createGraphics(), p);
PersonDirector pdFat = new PersonDirector(pfb);
pdFat.createPerson();
替换Director类:
使用Director类是实现建造者模式的一种方式,还有一种方式是在PersonBuilder类中创建一个build()方法,直接创建对象
abstract class PersonBuilder {
protected Graphics;
protected Pen p;
public PersonBuilder(Graphics g, Pen p) {
this.g = g;
this.p = p;
}
public abstract void buildHead();
public abstract void buildBody();
public abstract void buildArmLeft();
public abstract void buildArmRight();
public abstract void buildLegLeft();
public abstract void buildLegRight();
public void build() {
buildHead();
buildBody();
buildArmLeft();
buildArmRight();
buildLegLeft();
buildLegRight();
}
}
二、动机
将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。
**主要解决:**主要解决在软件系统中,有时候面临着"一个复杂对象"的创建工作,其通常由各个部分的子对象用一定的算法构成;由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将它们组合在一起的算法却相对稳定。
**何时使用:**一些基本部件不会变,而其组合经常变化的时候。
优点:
- 建造者独立,易扩展。
- 便于控制细节风险。
缺点:
- 产品必须有共同点,范围有限制。
- 如内部变化复杂,会有很多的建造类。
三、与工厂模式的区别
- 解决的问题不同
- 建造者模式要解决的问题是把复杂对象的构建与它的表示分离,通过定义抽象建造类的方式把构建对象的所必须要经历的步骤固化下来,避免在创建对象时因为疏忽而出现问题
- 工厂模式解决的问题是提供一种 “封装机制” 来避免客户程序和这种 “具体对象创建工作” 的紧耦合,使得对象创建发生变化的时候,不影响程序的主流程
- 粒度不同
- 在建造者模式中,一个具体产品的产生是依赖各个部件的产生以及装配顺序,它关注的是“由零件一步一步地组装出产品对象”。
- 在工厂方法模式里,我们关注的是一个产品整体,如超人整体,无须关心产品的各部分是如何创建出来的;
简单地说,工厂模式是一个对象创建的粗线条应用,建造者模式则是通过细线条勾勒出一个复杂对象,关注的是产品组成部分的创建过程。