ps:本文系为转载,阅读原文可获取源码,文章末尾有原文链接
ps:这一篇是写建造者模式、原型模式和适配器模式
**1、建造者模式
**
指将一个复杂对象的构造与它的表示分离,使同样的构建过程可以创建不同的表示;它是将一个复杂的对象分解为多个简单的对象,然后一步一步构建而成;建造者模式注重零部件的组装过程。
建造者模式由以下几个角色组成:
(1)产品角色:它是一个复杂对象,包含多个组成部件,由具体建造者来创建其各个零部件。
(2)抽象建造者:它是一个包含创建产品每个部件的抽象方法的抽象类,一般还提供返回复杂产品的方法。
(3)具体建造者:它是抽象建造者的子类,完成复杂产品的各个部件的具体建造方法。
(4)指挥者:它调用建造者对象中的部件构造和组装方法完成复杂对象的建造,包含定义组成部件的类,包括将这些部件装配成最终产品的接口。
产品角色,新建一个产品类 AirShip,它包含 OrbitalModel、Engine engine 和 EscapeTower 这几个部件;
public class AirShip {
private OrbitalModel orbitalModel;
private Engine engine;
private EscapeTower escapeTower;
public OrbitalModel getOrbitalModel() {
return orbitalModel;
}
public void setOrbitalModel(OrbitalModel orbitalModel) {
this.orbitalModel = orbitalModel;
}
public Engine getEngine() {
return engine;
}
public void setEngine(Engine engine) {
this.engine = engine;
}
public EscapeTower getEscapeTower() {
return escapeTower;
}
public void setEscapeTower(EscapeTower escapeTower) {
this.escapeTower = escapeTower;
}
}
class OrbitalModel {
private String name;
public OrbitalModel(String name) {
super();
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
class Engine {
private String name;
public Engine(String name) {
super();
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
class EscapeTower {
private String name;
public EscapeTower(String name) {
super();
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
抽象建造者,新建一个 AirShipBuilder 接口,包含创建 OrbitalModel、Engine engine 和 EscapeTower 这几个部件的抽象方法;
public interface AirShipBuilder {
public OrbitalModel builderOrbitalModel();
public Engine builderEngine();
public EscapeTower builderEscapeTower();
}
具体建造者,新建一个类 MyAirShipBuilder 并实现 AirShipBuilder;
public class MyAirShipBuilder implements AirShipBuilder{
@Override
public OrbitalModel builderOrbitalModel() {
System.out.println("轨道舱");
return new OrbitalModel("我的轨道舱");
}
@Override
public Engine builderEngine() {
System.out.println("发动机");
return new Engine("我的发动机");
}
@Override
public EscapeTower builderEscapeTower() {
System.out.println("逃逸塔");
return new EscapeTower("我的逃逸塔");
}
}
指挥者,新建一个类 MyAirShipDirector 并实现 AirShipDirector 接口,在当前类中用 directorAirShip 方法建造部件和组装产品;
public interface AirShipDirector {
public AirShip directorAirShip();
}
public class MyAirShipDirector implements AirShipDirector{
private AirShipBuilder builder;
public MyAirShipDirector(AirShipBuilder builder) {
super();
this.builder = builder;
}
@Override
public AirShip directorAirShip() {
Engine engine = builder.builderEngine();
OrbitalModel orbitalModel = builder.builderOrbitalModel();
EscapeTower escapeTower = builder.builderEscapeTower();
AirShip airShip = new AirShip();
airShip.setEngine(engine);
airShip.setEscapeTower(escapeTower);
airShip.setOrbitalModel(orbitalModel);
return airShip;
}
}
在客户端测试
AirShipDirector director = new MyAirShipDirector(new MyAirShipBuilder());
AirShip airShip = director.directorAirShip();
System.out.println(airShip.getEngine().getName());
System.out.println(airShip.getEscapeTower().getName());
System.out.println(airShip.getOrbitalModel().getName());
建造者模式封装性好,扩展性好,每个建造者相互独立互补影响,便于解耦;在调用客户端端时不必理会产品内部组成的细节,但是建造的部件和产品内部的部件必须相同,限制了建造的范围;如果产品内部部件发生变化,那么建造者也需要跟着更改。
2、原型模式
用一个已经存在的实例作为原型,创建一个和原型相同类型的对象不需要 new,通过复制该原型对象就可以创建一个和原型相似的新对象;这里的原型对象和新对象不是同一个对象,而是它们同属一个类。
原型模式有以下角色:
(1)抽象原型类:具体原型对象必须实现的接口 Cloneable。
(2)具体原型类:实现抽象原型类的 clone() 方法,返回一个被复制的对象。
(3)访问类:也就是客户端类调用具体原型类中的 clone() 方法来完成新的对象拷贝。
原型模式的克隆划分为浅克隆和深克隆;浅克隆就是对象的 clone 方法只会复制对象属性中的基本的数据类型(8种基本数据类型),不会复制非基本的数据类型,非基本的数据类型还是指向原型对象属性的内存地址;深克隆就是把非基本数据类型的属性也给复制过去,非基本数据类型的对象不再指向原型对象的属性地址;下面对2种克隆进行举例;
2、1 浅克隆
由于我是用 Java 代码写的,而 Java 没有对指针进行扩展,所以这个基本数据类型不好验证,我们就验证上面所说的非基本的数据类型还是指向原型对象属性的内存地址。
(1)抽象原型类,它是 Cloneable 接口,Java 内部提供,不用自己去新写:
package java.lang;
public interface Cloneable {
}
(2)具体原型类,新建一个 Sheep 类并实现 Cloneable 接口:
public class Sheep implements Cloneable{
private String name;
private Date birthday;
public Sheep() {
System.out.println("Sheep类对象无参构造方法");
}
public Sheep(String name, Date birthday) {
super();
this.name = name;
this.birthday = birthday;
System.out.println("Sheep类对象有2个参数的构造方法");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
@Override
public Object clone() throws CloneNotSupportedException {
Object object = super.clone();
return object;
}
}
(3)客户端类,新建一个 Test 类,对 Sheep 类的原型对象进行克隆:
public class Test {
public static void main(String[] args) {
Date date = new Date(12345689l);
String objectString = "原型对象----";
String objectString2 = "新对象----";
Sheep s1 = new Sheep("多利",date);
System.out.println(objectString + s1);
System.out.println(objectString + s1.getName());
System.out.println(objectString + s1.getBirthday());
System.out.println("________________________________");
try {
Sheep s2 = (Sheep) s1.clone();
date.setTime(345466578887l);
System.out.println(objectString2 + s2);
System.out.println(objectString2 + s2.getName());
System.out.println(objectString2 + s2.getBirthday());
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}
日志打印如下所示:
从日志可以看出,原型对象和新对象不是同一个对象,当我们把原型对象克隆给新对象之后,再更改原型对象的属性 date 的内容,发现新对象的属性 date 内容也更改了,这就论证了上面说的,浅克隆不会复制非基本的数据类型。
2、2 深克隆
我们来验证非基本数据类型的对象不再指向原型对象的属性地址这句话。
(1)在上面浅克隆代码的基础上,我们把 Sheep 类的 clone 方法修改一下,其他的不变:
@Override
public Object clone() throws CloneNotSupportedException {
Object object = super.clone();
Sheep sheep = (Sheep) object;
sheep.birthday = (Date) this.birthday.clone();
return object;
}
(2)客户端类不变,还是和浅克隆的示例一样调用:
Date date = new Date(12345689l);
String objectString = "原型对象----";
String objectString2 = "新对象----";
Sheep s1 = new Sheep("多利",date);
System.out.println(objectString + s1);
System.out.println(objectString + s1.getName());
System.out.println(objectString + s1.getBirthday());
System.out.println("________________________________");
try {
Sheep s2 = (Sheep) s1.clone();
date.setTime(345466578887l);
System.out.println(objectString2 + s2);
System.out.println(objectString2 + s2.getName());
System.out.println(objectString2 + s2.getBirthday());
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
日志打印如下:
客户端这边先对原型对象进行克隆得到新对象,然后再改变原型对象 date 的内容,再输出新对象的信息,发现新对象的 date 内容和原型对象刚初始化 date 内容一样,说明了深克隆实现了非基本数据类型的对象不再指向原型对象的属性地址。
使用深克隆方式保存对象的状态,使用原型模式拷贝,将其状态保存起来,简化了创建对象的过程;每个被克隆的类必须实现 Cloneable 接口并重写 clone 方法,从2个克隆的案例中可以看出,使用原型模式拷贝对象不会调用类的构造方法,因为是通过调用 Object 类的 clone 方法实现的;原型模式和单例模式一起用,单例模式就会失效,因为访问权限 private 都对原型模式无效。
**3、适配器模式
**
将一个类的接口转换成客户期望的另一个接口,它使得原本因为接口不兼容、不能一起工作的一些类可以在一起工作。
适配器模式有以下角色:
(1)目标接口:客户所期待的接口,目标可以是抽象类,也可以是接口,目标接口的实现类会使用接口方法调用适配者。
(2)适配者:它是被调用和适配的现存组件库中的组件接口。
(3)适配器:它是一个转换器,实现了目标接口,通过继承或引用适配者的对象,把适配者接口转换成目标接口,让客户用目标接口来调用适配者。
适配器模式可分为类适配器模式和对象适配器模式,类适配器模式就是适配器继承适配者,用接口方法调用适配器继承适配者过来的方法;对象适配器模式就是采用对象组合方式实现,适配器和适配者不存在关系,用接口方法调用适配者的方法。
**3、1 类适配器模式
**
(1)目标接口,新建一个接口 TargetInterface:
public interface TargetInterface {
public void request();
}
(2)适配者,新建一个类 AdapteeClass:
public class AdapteeClass {
public void performRequest() {
System.out.println("适配者的performRequest方法被执行");
}
}
(3)适配器,新建一个类 AdapterClass,继承 AdapteeClass 并实现 TargetInterface 接口:
public class AdapterClass extends AdapteeClass implements TargetInterface{
@Override
public void request() {
performRequest();
}
}
(4)在客户端进行调用:
TargetInterface targetInterface = new AdapterClass();
targetInterface.request();
**3、2 对象适配器模式
**
(1)在类适配器模式的情况下,我们不再新建目标接口和适配者,这里新建另外一个适配器,类名叫 Adapter2Class 并实现目标接口 TargetInterface:
public class Adapter2Class implements TargetInterface{
private AdapteeClass adapteeClass;
public Adapter2Class(AdapteeClass adapteeClass) {
this.adapteeClass = adapteeClass;
}
@Override
public void request() {
adapteeClass.performRequest();
}
}
(2)在客户端进行调用:
AdapteeClass adapteeClass = new AdapteeClass();
TargetInterface targetInterface = new Adapter2Class(adapteeClass);
targetInterface.request();
对象适配器模式中的适配器拥有一个适配者对象的属性,把这个具体的特殊功能委托给适配者对象来实现;使用对象适配器模式,可以使得适配器根据传入的适配者对象或者适配者子类对象达到适配多个不同被适配的功能;不管是类适配器模式还是对象适配器模式,它具有灵活性,只需修改适配器类和客户端的代码就可以了。
对于对象适配器来说,更换适配器的实现过程不简单;对于类适配器来说,比较单一,一个适配器只能有一个适配者;通过适配器,客户端可以调用同一接口,对客户端来说是透明的;类适配者复用了现存的类,开发者不需要修改原有代码而重用现有的适配者类;将目标类和适配者类解耦,解决了目标类和适配者类接口不一致的问题,就好比插座口和手机充电口插头。