共计 7627 个字符,预计需要花费 20 分钟才能阅读完成。
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();
对象适配器模式中的适配器领有一个适配者对象的属性,把这个具体的非凡性能委托给适配者对象来实现;应用对象适配器模式,能够使得适配器依据传入的适配者对象或者适配者子类对象达到适配多个不同被适配的性能;不论是类适配器模式还是对象适配器模式,它具备灵活性,只需批改适配器类和客户端的代码就能够了。
对于对象适配器来说,更换适配器的实现过程不简略;对于类适配器来说,比拟繁多,一个适配器只能有一个适配者;通过适配器,客户端能够调用同一接口,对客户端来说是通明的;类适配者复用了现存的类,开发者不须要批改原有代码而重用现有的适配者类;将指标类和适配者类解耦,解决了指标类和适配者类接口不统一的问题,就好比插座口和手机充电口插头。