创建模式
创建模式是对类的实例化过程的抽象。可以动态决定如何创建对象。
简单工厂(Simple Factory)
简单工厂模式又叫构造方法
//build the first part of the product
}
public void buildPart2(){//产品零件2构造方法
//build the second part of the product
}
public Product retrieveResult(){//产品返还
return product;
}
}
导演者角色是与客户端打交道的角色。导演者角色将客户端创建产品的请求划分为对各个零件的建造请求,再将这些请求“委派”给具体建造者角色。导演者角色并没有产品类的具体知识,真正拥有产品类的具体知识的是具体建造者角色。
public class Director{//导演者
private Builder builder;
public Director(Builder builder){//可以动态地更换建造器
this.builder = builder;
}
//产品构造方法,负责协调调用各个零件的建造方法
public void construct(){
builder = new ConcreteBuilder();
builder.buildPart1();//构造零件1
builder.buildPart2();//构造零件2
builder.retrieveResult();//产品返还
//continue with other code
}
}
虽然客户端依赖于某个具体建造者,但是操纵具体建造者的任务却是属于导演者对象的。把创建具体建造者对象的任务交给客户端而不是导演者对象,是为了将导演者对象与具体建造者对象的耦合变成动态的,从而使导演者对象可以操纵多个具体建造者对象中的任何一个。
public class Client {//场景
public static void main() {
Builder builder = new ConcreteBuilder();//建造者
Director director = new Director(builder);//导演者
director.construct();//向导演者发出建造产品命令
}
}
示意性类图中的建造模式只是一个最简草的例子,它包括了一个具体建造者类和一个产品类。在实际应用中,一般会有一个以上的具体建造者类和相应的一个以上的产品类。
多个产品
上面只有一个产品的情况,如果有两个产口类的话,就应当有两个具体建造者类。
产品类Product1和Product2各有两个零件,但它们有不同的内部表象。在这里建造模式提供了一个对建造过程的封装,使得客户端不需要知道产品类的建造细节,便可以使用一个建造接口生成具有不同的内部表象的Product1和Product2对象。
如果Product1和Product2不具有相同接口时,则Builder接口中的retrieveResult就需要返还不同的类型,解决办法有两种:
第一种:强迫Product1和Product2实现同一标示性接口,就是上面所示。
第二种:如果产品类是第三提供的,正不能修改,那么就只好将retrieveResult从抽象建造者中移到具体建造者中,但如果Direcotr对象需要调用这个方法,则需要先将Builder向下转型。
必须指出的是,这种没有抽象产品类或者公共产品接口的情况更为普遍。
在多产品的建造者模式中,如果有一些产品有较多的零件,而有些产品有较少的零件,建造模式还可以使用吗?回答是肯定可以使用的。这些产品并不一定要有同样数目的零件才可以使用建造模式。如果一个产品有较少的零件,可以使用空的零件建造方法,忽略没有的零件。当然可以先为Builder设计一个缺少适配器,再让具体构造都从它继承。
变体
省略抽象建造者
如果肯定系统只需要一个具体建造者的话,可以省略掉抽象建造者,因为抽象建造者角色存在的目的是规范具体建造者角色的行为。
省略导演者
在个体建造者只有一个的情况下,如果抽象建造角色已经被省略掉,那么还可以进一步活力掉导演者角色。此时具体构建者自己扮演了导演者和建造者双重角色。此时需要将原导演者里的相关代码移到建造者中。
如果只省略掉导演者,而没有省略掉抽象建造者时,此时的抽象建造者相当于模板方法:
public class Builder {
private Product product = new Product();
public void buildPart1() {
// Write your code here
}
public void buildPart2() {
// Write your code here
}
public Product retrieveResult() {
return product;
}
//从导演者中移过来,此时相当于模板方法
public void construct() {
buildPart1();//具体怎么构造由子类去实现
buildPart2();
Product product = retrieveResult();
}
}
合并建造者角色和产品角色
当建造模式失去抽象建造者与导演者之后,还可以进一步退化——去掉具体建造者。此时具体建造者与产品合并,从而使得产品自己就是自己的建造者。显然,这样做混淆了对象的创建者与对象本身。但是有时候一个产品对象有着固定的几个零件,而且永远只有这几个零件,此时将产品类与建造类合并是很好的,这样可以简化系统的设计,此时这些产品的性质(属性)就变成了零件。比如JavaMail库中的Message类就是一个退化的建造模式,它的性质如from、recipient、subject、text等,都可看做是Message的“零件”。
应用场景
需要生成的产品对象有复杂的内部结构。
需要生成的产品对象的属性相互依赖。
原型(Prototype)模式
通过给出的原型对象来指明所要创建的对象的类型,然后用复制这个原型对象的办法创建出更多同类型的对象。这就是原型模式的用意。
不通过new关键字来产生一个对象,百是通过对象复制来实现的模式就叫原型模式。
GoF:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
Java语言直接支持原型模式。
模式结构
public interface Prototype extends Cloneable {//原型接口
Object clone();
}
public class ConcretePrototype implements Prototype {//具体原型
public Object clone() {
try {
return super.clone();
} catch (CloneNotSupportedException e) {
//write your code here
return null;
}
}
}
public class Client {
private Prototype prototype;
public void operation(Prototype example) {
Prototype p = (Prototype) example.clone();
}
}
应用场景
当一个系统应该独立它的产品类结构时,要使用Prototype模式。比如类是在运行期间动态加载的,我们不知道这个类的具体类型,如果再创建它的实例是很难的,这时如果它具有克隆能力(Java语言本身就具有,不过在某些情况一定要重写clone方法,因为防止浅克隆),我们就很容易地创建出这个对象的另一个实例,这时我们完成了不可能通过new方式来完成的操作。
l 当要实例化的类是在运行时刻指定时,例如,通过动态加载;或者
l 为了避免创建一个与产品类层次平行的工厂类层次时;或者
l 当一个类的实例只能有几个不同状态组合中的一种时。建立相应数目的原型并克隆它们可能比每次用合适的状态手工实例化该类更方便一些。
为了线程安全,给各个线程一个拷贝。
原型模型很少单独出现,一般和工厂方法模式一起出现,通过clone的方法创建一个对象,然后由工厂方法提供给调用者。
优点
l 原型模型允许动态地增加或减少产品类。由于创建产品类实例的方法是产品内部具有的,因此,增加新产品对象整个结构没有影响(因为本身系统中没有直接使用这些具体的产品类)。
l 原型模式提供简化的创建结构。工厂方法模式常常需要有一个与产品类等级结构相同的等级结构,而原型模式就不需要这样,它可以自行克隆自己,不必借助于工厂方法,它本身就是一个工厂方法。
l 具有给一个应用软件动态加载新功能的能力。
l 产品类不需要非得有任何事先确定的等级结构,因为原型模式适用于任何的产品等级结构。
原型模型是对内存二进制流的拷贝,要比直接new一个对象性能好很多(而且也比序化方式要快),特别是要在一个循环体内产生大量的对象时。