设计模式建造者模式

Posted Nothing Is Given.

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了设计模式建造者模式相关的知识,希望对你有一定的参考价值。

GOF论述了23种设计模式,它们有3个分类————创建型模式、结构型模式、行为模式。
此篇为创建型模式
创建型模式抽象了实例化的过程,它们可以帮助某个系统独立于如何创建、组合以及表示该系统所包含的对象。
当系统向着更加依赖于对象复合和不是类的继承演变时,创建型模式就变得更为重要。

创建型模式始终围绕两个基本点展开:
1、他们都将某个系统使用到了哪些具体的类这项信息封装起来;
2、创建型模式进一步隐藏了哪些具体的类的实例是如何被创建并放到一起的。
由此,整个系统所了解的仅仅是关于这些对象的抽象类所提供的接口。

所以,创建型模式所关心的问题是:
*创建了什么?
*由什么来创建?
*创建的方式是什么?
*在什么时间去创建?

工厂模式
***************************************************************************
简单工厂模式(静态工厂方法)(非GOF的23种模式)
定义:由一个工厂对象决定创建哪一种产品类的实例。(例如工厂对象中的switch-case结构,直接选择创建哪种产品实例)
优点:分离了客户端和后台逻辑,去除了客户端与具体产品的依赖
缺点:当需要增加一个产品时,需要添加一个产品类,同时也需要修改简单工厂类————对扩展开放,同时也对修改开放,不符合开放-封闭原则。

反思:如何保持简单工厂可扩展的方便性,又对修改关闭呢?
对简单工厂进行升级!。

工厂方法模式
定义:定义一个用于创建对象的接口,让子类决定实例化哪一个类。
说明:工厂方法模式使得一个类的实例化延迟到子类。

解析:
首先,定义的工厂接口,其中有一个抽象方法,这个方法就是子类需要重写用来创建产品实例的。
然后,我们需要为每一个不同产品均创建一个实现该接口的工厂方法,然后重写接口中的抽象方法,实例化对应的产品,并返回该实例。
最后,在客户端通过多态,先获得具体产品对应的工厂方法对象,然后调用该工厂方法对象中的方法,获得具体的产品对象。

优点:
易于添加新产品;
后台模块契合了开放-封闭原则;
缺点:
每增加一个产品,需要添加一个产品类,以及这个产品类对应的工厂方法类,增加了额外的开发量。
客户端部分仍然违反开放-封闭原则,只是把后台判断逻辑移动到了前台。

适用场景:
|------当一个类不知道它所必须创建的对象的类时。
|------当一个类希望由它的子类来指定它所创建的对象时。
|------当类将创建对象的职责委托给多个帮助子类中的某一个,并且希望将哪一个帮助子类是代理者这一信息局部化时。

简单工厂和工厂方法的比较:
产品较少时,简单工厂模式就能够满足要求;
产品比较丰富时,过多的分支不利于程序的维护,工厂方法模式有利于降低程序为维护量。

***************************************************************************

抽象工厂模式(与反射技术结合避免缺陷)
定义:
提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
抽象工厂模式是面向对象编程中,所有形态的工厂模式中最为抽象和最具一般性的一种形态。

需要解决的问题:
现实生活中,产品一般分成一个个的族,每个产品族下又分为不同的产品系列。
软件中,也经常遇到这种情况,所以重点要解决的问题是————如何创建一系列相关或相互依赖的对象?
在工厂方法模式中,假设我们只有一个族下有三个产品,那我们需要创建1个抽象产品基类,衍生的3个产品类,1个工厂接口,衍生的3个产品工厂方法类,这里总共8个类;
若我们需要新增一个产品族,其中也有3个产品,那么我们就需要新编写8个类!
如果产品族增多,那么工作量也会越来越大。如何才能减小工作量呢?
那就是,我们在产品族1中的工厂接口中,增加一个返回产品族2中的产品的接口方法。
现在,这个工厂接口中有两个接口方法了,一个返回族A中的产品实例,一个返回族B中的产品实例。
而我们希望的是,实现这个接口的工厂方法所能生产的产品,如A1和B1,是有关联的(不然为什么把他们放到同一个工厂中呢?)

缺点:
抽象工厂模式在便于交换产品系列的同时,在改动声明过产品类的地方进行大量的修改,显然不是一个好的方法。
即难以支持新种类的产品,抽象工厂接口确定了可以被创建的产品集合。新种类的产品若要加入这个工厂(如族C的产品C1要加入A1、B1所在的工厂),需要扩展抽象工厂接口,这就涉及到了接口本身和所有实现类的改变。

说明:
使用抽象工厂模式时,如果客户端有比较大量的分支判断,使用反射技术代替if和switch语句会节省大量的工作量,而且能有效增加程序的灵活性。
|------反射技术的优缺点、安全性。
结合反射技术也可以解决客户端违反开放-封闭原则的问题。(对于简单工厂模式和工厂方法模式同样适用)


***************************************************************************

建造者模式————将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示
|-----使用建造者模式,用户只需要指定要创建的类型就可以得到相应的对象,而具体的建造过程和细节被Director和Builder隐藏了,这正是依赖倒转原则的体现:抽象不应该依赖于细节,细节应该依赖于抽象。

适用场景:
|------当需要把构建对象的实例的逻辑转移到类的外面去,在这个类的外部去定义其构造逻辑时。
|------当需要按照某种步骤去构造一个对象时。

说明:建造者模式的精髓就是将构造逻辑分离,而把构造方法作为一个完整的单独任务去完成,甚至也可以创建单独的层次结构来完成对象的构造工作。

***************************************************************************

工厂模式和建造者模式的比较:
|-----目的都是为了得到一个对象。
|-----工厂模式的侧重点在于将对象的实例化延迟到子类中,创建者模式的侧重点则是保持一个对象创建过程的稳定性。
|-----二者都将对象的创建过程与客户进行隔离,工厂模式专注创造相同表象的对象,但当希望得到具有多种不同表象的实例时,建造者模式是不二选择。

***************************************************************************

单例模式————保证一个类仅有一个实例,并提供一个访问它的全局访问点。
|-----多线程环境中并不能保证只产生一个实例。
|-----引入锁机制,即需要使用Lock类。Lock的作用是构造一块临界区来控制线程对代码的访问,确保每次只有一个线程运行到临界区的代码。
|-----单实例只是实例有限类的一种特殊情况,可以有双实例、多实例。

适用场景:
|------当类只能有一个实例而且第三方可以从一个公共访问点访问它时。
|------当一个唯一的实例可以通过子类化来扩展,而且第三方需要在不更改代码的情况下就能使用一个扩展的实例时。


***************************************************************************

原型模式————用原型实例指定创建对象的种类,并且通过拷贝这些原型来创建新的对象。
|-----原型模式是用来解决实例对象的拷贝问题,即依托一个已经实例化的对象去创建另一个可以进行定制的对象,而不需要知道创建过程中的具体细节。
|-----使用原型模式,不仅仅是使得代码变得简洁这么一个好处。在代码运行过程中,每一次实例化new对象,都需要执行一次构造函数,如果构造函数比较复杂,那么多次执行构造函数必然会影响性能。

在初始化信息没有改变的情况下,克隆就是最好的进行实例化的方法。这样做不仅仅是隐藏了对象创建的具体细节,更重要的是大大提高了运行的效率。
|-----在Java中,由于克隆非常常用,所以Java提供了Cloneable接口,所以编码时只需要实现Cloneable接口就可以使用原型模式。
|-----原型模式最本质的特点是,在不重新进行初始化对象的情况下动态获取对象在运行时的状态。
|-----原型模式并不是线程安全的,因此在多线程环境下使用时,需要特别注意。

深拷贝与浅拷贝————当一个实例对象中拥有一个其他类的对象属性
|-----问题:clone()方法对于值类型的字段会进行逐位复制,而对于引用类型的字段,会复制引用而不是复制该引用指向的对象,所以原始对象和副本对象拥有的是同一个对象
|-----浅拷贝:副本对象的所有变量都含有与原始对象中变量有相同的值,包括引用变量的值。
|-----深拷贝:副本中的引用变量指向一个新的对象,而不是与原始对象中的引用变量指向相同的对象。
|-----新问题:如果原始对象中引用变量指向的对象中又包含其他对象,怎么办?这涉及到了复制深度的问题,究竟需要复制多少层,在设计前就需要好好考虑。

数组数据的拷贝
|-----使用赋值运算符“=”去拷贝数组的方式是错误的,因为数组本身是一个对象。
|-----使用System.arraycopy()方法。

原型模式的适用场景:
|-----要实例化的类在运行时刻指定时。
|-----为了避免创建一个与产品类层次平行的工厂类层次时。
|-----当一个类的实例只能有几个不同状态组合中的一个时。

总结:
|-----有些场合我们需要将类的使用者与构造函数进行分离,也就是说,我们不允许类的使用者直接调用一个构造方法,而创建型模式大多就是为了这一目的产生的。
|-----原型模式同样对使用者隐藏了对象创建的细节,但是与其他创建型模式不同的是,其它是通过一个类进行实例化来创建新的对象,而原型模式是通过复制一个已经存在的对象来生成新的对象。
|-----在决定复制的深度时,一些比较大的项目中可能会出现循环引用的问题,需要程序员结合实际项目进行深层次的考虑。

***************************************************************************

 

以上是关于设计模式建造者模式的主要内容,如果未能解决你的问题,请参考以下文章

建造者模式

设计模式之建造者模式(创建型)

《大话设计模式》——建造者模式

设计模式—建造者模式(Builder)

建造者模式(Builder Pattern)

设计模式最终总结建造者模式