一、建造者模式
本质:分离了对象子组件的单独构造(由Builder负责)和装配的分离(由Director负责),从而可以构建出复杂的对象,这个模式适用于:某个对象的构建过程十分复杂
好处:由于构建和装配的解耦,不同的构建器和相同的装配可以做出不同的对象,相同的构建器和不同装配顺序也可以组成不同的对象,实现了构建算法和装配算法的解耦,
实现了更好的复用。
【基本模块】
/**** "宇宙飞船"类*/ package cn.sxt.builder;public class Airship {private OrbitalModule orbitalModule;//轨道仓模块private Engine engine;//发动机模块private EscapeTower escapeTower;//逃逸塔模块public OrbitalModule getOrbitalModule() {return orbitalModule;}public void setOrbitalModule(OrbitalModule orbitalModule) {this.orbitalModule = orbitalModule;}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;}public String toString() {String msg="配置:["+orbitalModule.getName()+engine.getName()+escapeTower.getName()+"]";return msg;}public void launch() {System.out.println(engine.getName()+"点火,5,4,3,2,1,发射!");} }class OrbitalModule{//"轨道仓"类private String name;public OrbitalModule(String name) {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) {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) {this.name = name;}public String getName() {return name;}public void setName(String name) {this.name = name;} }
【建造者和装配者】接口
/**** "建造类"接口,提供构建3个子类的方法*/ package cn.sxt.builder;public interface AirshipBuilder {OrbitalModule builderOrbitalModule();cn.sxt.builder.Engine builderEngine();EscapeTower builderEscapeTower(); }/**** "装配类"接口, Director:导演,负责人*/ package cn.sxt.builder;public interface AirShipDirector {Airship directorAirship();//组装飞船对象 }
【建造者和装配者】具体实现类
/**** "神7飞船"子组件的建造者*/ package cn.sxt.builder;public class S7AirShipBuilder implements AirshipBuilder { //XML解析中,JDOM库中的类:DOMBuilder也是建造者模式public OrbitalModule builderOrbitalModule() { System.out.println("--制造天宫牌轨道仓--");return new OrbitalModule("天宫牌轨道仓 ");}public Engine builderEngine() {System.out.println("--制造盘古牌发动机--");return new Engine("盘古牌发动机 "); }public EscapeTower builderEscapeTower() {System.out.println("--制造曹操牌逃逸塔--");return new EscapeTower("曹操牌逃逸塔");}}
/**** "神7飞船"装配者*/ package cn.sxt.builder;public class S7AirShipDirector implements AirShipDirector{private AirshipBuilder builder;//要装配的对象,要装配什么组件public S7AirShipDirector(AirshipBuilder builder) {this.builder = builder;}public Airship directorAirship() {OrbitalModule oModule=builder.builderOrbitalModule();Engine engine=builder.builderEngine();EscapeTower eTower=builder.builderEscapeTower();//获得各个组件 Airship ship=new Airship();//一个具体的飞船对象 ship.setOrbitalModule(oModule);ship.setEngine(engine);ship.setEscapeTower(eTower);return ship;}}
【客户端】
/**** 客户端*/ package cn.sxt.builder;public class Test_0424_Client {public static void main(String[] args) {AirshipBuilder builder=new S7AirShipBuilder();//飞船建造者AirShipDirector director=new S7AirShipDirector(builder);//飞船装配者 Airship s7ship=director.directorAirship();System.out.println(s7ship);s7ship.launch();}}
二、原型模式(克隆模式、原型模式。prototype:原型、雏形)
通过new产生一个对象需要非常繁琐的数据准备或者访问权限,则可以使用原型模式。JavaScript中的继承中使用过。
就是Java中的克隆技术,以某个对象为原型。new创建新的对象采用默认值。克隆出的对象的属性值完全和原型相同,并且克隆出的新对象改变不会影响原型对象。
然后,再修改克隆对象的值。
实现:通过Cloneable接口和clone方法,帮我们进行内存的复制操作。常常与工厂模式结合起来
用途:如果短时间需要创建大量对象,并且new的时候比较耗时间,可以用原型模式,效率大概是普通方法的100倍
【原型羊】
/**** 原型"羊"类*/ package cn.sxt.prototype; import java.util.Date;public class Sheep implements Cloneable {private String name;private Date birthday;@Override //重写父类中的clone方法protected Object clone() throws CloneNotSupportedException {Object obj=super.clone();//直接调用Object类对象的clone方法/* //添加如下代码2行实现深克隆Sheep s=(Sheep)obj;s.birthday=(Date)this.birthday.clone();//把属性(出生日期)也进行克隆 */ return obj;}public Sheep() {super();}public Sheep(String name, Date birthday) {super();this.name = name;this.birthday = birthday;}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;}}
【克隆羊】
/*** 测试取克隆一只"羊"(浅克隆和深克隆)*/ package cn.sxt.prototype;import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date;public class Test_0424_Client01 {public static void main(String[] args) throws Exception {Date date=new Date(1000000L);Sheep s1=new Sheep("羊一代",date);//要克隆出新羊,需要原型"羊",这就是原型"羊" System.out.println(s1);System.out.println(s1.getName());System.out.println(s1.getBirthday());Sheep s2=(Sheep)s1.clone();//新建一个对象,但不是通过new,而是直接调用原型"羊"的clone方法System.out.println(s2);//输出结果显示s1与s2对象在内存中的值不同,但是属性信息等一模一样 System.out.println(s2.getName());System.out.println(s2.getBirthday());s2.setName("羊二代");//修改s2的属性值,完全不影响s1的值 System.out.println(s2.getName());//修改原型"羊"的出生日期,看看克隆羊的出生日期/** 浅克隆:把原型羊的出生日期改了,但是影响克隆羊的出生日期* 深克隆:把原型羊的出生日期改了,但是不影响克隆羊的出生日期* 原理:* 浅克隆:s1----->data对象<-----s2(s1和s2的出生日期均执行同一时间对象data,s1一改动,s2也受影响)* 深克隆:s1----->data对象。data对象的复制品<-----s2,s1改动与s2没有关系,s2的出生日期不会动* */date.setTime(40000000L);System.out.println(s1.getBirthday());System.out.println(s2.getBirthday());} }
【深克隆方式之二】
/**** 用序列化和反序列化实现深克隆*/ package cn.sxt.prototype;import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.Date;public class Test_0424_Client02 {public static void main(String[] args) throws Exception {Date date=new Date(1000000L);Sheep s1=new Sheep("羊一代",date);//要克隆出新羊,需要原型"羊",这就是原型"羊" System.out.println(s1);System.out.println("原型羊:"+s1.getBirthday());//Sheep s2=(Sheep)s1.clone();//新建一个对象,但不是通过new,而是直接调用原型"羊"的clone方法//深克隆方法之二:使用序列化和反序列化克隆一只羊ByteArrayOutputStream bos=new ByteArrayOutputStream();ObjectOutputStream oos=new ObjectOutputStream(bos);oos.writeObject(s1);byte[] buf=bos.toByteArray();ByteArrayInputStream bis=new ByteArrayInputStream(buf);ObjectInputStream ois=new ObjectInputStream(bis);Sheep s2=(Sheep)ois.readObject();System.out.println(s2);System.out.println("克隆羊:"+s2.getBirthday());//修改原型"羊"的出生日期,看看克隆羊的出生日期date.setTime(40000000L);System.out.println("修改原型羊的出生日期后:");System.out.println("原型羊:"+s1.getBirthday());System.out.println("克隆羊:"+s2.getBirthday());}}