java23种设计模式(二)-- 建造者模式和原型模式
一、建造者模式
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创造不同的表示。
特点:
(1)、在某些属性没有赋值之前,复杂对象不能作为一个完整的产品使用。比如汽车包括方向盘、车门、发动机等各部件,缺少了这些部件就不能生产使用。
(2)、对象的一些属性必须按照顺序赋值,比如汽车应有车架才能装车轮和其他部件。
建造者模式涉及到以下四个角色的概念:
- 抽象建造者角色:提供一个接口,规范产品对象的建造,一般由子类实现。一般来说,产品的组成部分数与建造方法数相同,即有多少组成部分,就有多少个建造方法。
- 具体建造者角色:该角色实现了抽象建造者抽象建造者接口,主要是实现所有声明的方法以及返回建造好的产品实例。
- 导演者角色:负责调用具体建造者按照顺序建造产品。导演者只负责调度,真正执行的是具体建造者角色。
- 产品角色:该角色是建造的复杂对象,提供基本方法。
具体实现
1、创建产品角色
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Production { 2 3 private String part1; 4 5 private String part2; 6 7 public String getPart1() { 8 return part1; 9 } 10 11 public void setPart1(String part1) { 12 this.part1 = part1; 13 } 14 15 public String getPart2() { 16 return part2; 17 } 18 19 public void setPart2(String part2) { 20 this.part2 = part2; 21 } 22 23 @Override 24 public String toString() { 25 return "Production{" + 26 "part1='" + part1 + '\'' + 27 ", part2='" + part2 + '\'' + 28 '}'; 29 } 30 }
2、创建抽象建造者角色
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public interface IBuilder { 2 3 //产品有多少个组件,就有多少个建造方法 4 void buildPart1(); 5 6 void buildPart2(); 7 8 //返回产品类 9 10 Production build(); 11 12 }
3、创建具体建造者角色
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class BuilderA implements IBuilder{ 2 3 private Production production = new Production() ; 4 5 @Override 6 public void buildPart1() { 7 System.out.println("兰博基尼建造第一部分"); 8 this.production.setPart1("This is part1 of Lamborghini"); 9 } 10 11 @Override 12 public void buildPart2() { 13 System.out.println("兰博基尼建造第二部分"); 14 this.production.setPart2("This is part2 of Lamborghini"); 15 } 16 17 @Override 18 public Production build() { 19 System.out.println("咔擦!兰博基尼已造好!"); 20 return this.production; 21 } 22 }
4、创建导演者角色
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Director { 2 3 private IBuilder builder; 4 5 public Director(IBuilder builder){ 6 this.builder = builder; 7 } 8 9 /** 10 * 开始具体构造 11 */ 12 public Production construct(){ 13 this.builder.buildPart1(); 14 this.builder.buildPart2(); 15 16 return this.builder.build(); 17 } 18 }
5、客户端调用
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Client { 2 public static void main(String[] args) { 3 //建造一个产品A 4 IBuilder builder = new BuilderA(); 5 Director director = new Director(builder); 6 System.out.println(director.construct()); 7 } 8 }
运行结果
兰博基尼建造第一部分
兰博基尼建造第二部分
咔擦!兰博基尼已造好!
Production{part1='This is part1 of Lamborghini', part2='This is part2 of Lamborghini'}
优点
1)降低代码耦合度。在建造者模式中,客户端不需要知道产品内部是如何实现的,我们只需得到产品的对象。并且使用导演者和建造者分离组装过程和组件具体构造过程,具有灵活的扩展性。
2)优秀的扩展性。具体建造者相互独立,方便扩展,符合开闭原则。
缺点
1)一定的使用范围限制。建造者模式的产品的组件基本相同,如果产品的差异性较大,建造者模式就不适用了。
跟工厂方法模式对比:建造者模式和工厂模式同样是创建一个产品,工厂模式就是一个方法,而建造者模式有多个方法,并且建造者模式是有顺序的执行方法。就是说建造者模式强调的是顺序,而工厂模式没有顺序一说。
二、原型模式
通过复制一个已存在对象来生成一个新对象,被复制的对象称为原型;
补充:
1、JAVA中Object的clone方法已经为什么提供了复制对象的实现,且该方法为本地方法,性能好,在需要大量复制对象的情况,使用clone创建对象比new效率高;
补充下深拷贝和浅拷贝,深拷贝是通过拷贝内存(包括引用的对象)实现对象的创建;浅拷贝不拷贝引用的对象,但拷贝了引用的值,如果类的成员属性中都是基本类型,不含对象,也是可以达到深拷贝的效果;深拷贝可以通过将对象序列化成字节流以及反序列化实现,浅拷贝直接调用clone即可;
2、使用原型模式创建对象是没有调用类的构造方法的;
java已经很好的支持原型模式了,使用很简便,如下类,实现了Cloneable接口,即成了一个原型;
(参考另一篇博客:java的clone方法)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 package com.pichen.dp.creationalpattern.prototype; 2 3 public class Cell implements Cloneable{ 4 private int cellId; 5 public int getCellId() { 6 return cellId; 7 } 8 public void setCellId(int cellId) { 9 this.cellId = cellId; 10 } 11 public Cell(int id) { 12 this.cellId = id; 13 } 14 @Override 15 public Object clone() throws CloneNotSupportedException 16 { 17 System.out.println("clone a cell obj."); 18 return (Cell) super.clone(); 19 } 20 }
使用原型,复制10个拷贝:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 package com.pichen.dp.creationalpattern.prototype; 2 3 public class Main { 4 5 public static void main(String[] args) throws CloneNotSupportedException { 6 Cell prototypeCell = new Cell(888); 7 for(int i = 0; i < 10; i++){ 8 Cell copyCell = (Cell) prototypeCell.clone(); 9 System.out.println(copyCell.hashCode() + ":" + copyCell.getCellId()); 10 } 11 } 12 }