原型模式与建造者模式
原型模式与建造者模式
参考教程:https://www.bilibili.com/video/BV1G4411c7N4
代码实现 Github:https://github.com/yaokuku123/pattern
原型模式
- 案例
现在有一只羊tom,姓名为: tom, 年龄为:1,颜色为:白色,请编写程序创建和tom羊属性完全相同的10只羊。
- 传统方法
- Sheep类
package com.yqj.pattern.prototype.traditional;
public class Sheep {
private String name;
private int age;
private String color;
public Sheep(String name, int age, String color) {
this.name = name;
this.age = age;
this.color = color;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
@Override
public String toString() {
return "Sheep{" +
"name='" + name + '\'' +
", age=" + age +
", color='" + color + '\'' +
'}';
}
}
- Client类
package com.yqj.pattern.prototype.traditional;
public class Client {
public static void main(String[] args) {
Sheep sheep = new Sheep("tom", 1, "白色");
Sheep sheep2 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
Sheep sheep3 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
Sheep sheep4 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
Sheep sheep5 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
System.out.println(sheep);
System.out.println(sheep2);
System.out.println(sheep3);
System.out.println(sheep4);
System.out.println(sheep5);
}
}
- 分析
传统方法虽然可以实现相应的功能,但存在问题:不能动态的获取对象运行时的状态,而只是从初始化的对象中获取参数;若原对象增减属性的话,所有依此创建的新对象均需要修改;在创建新对象的时候,总是需要获取原始对象的属性,若对象的属性复杂时,效率低。
- 原型模式
解释:用原型实例指定创建对象的种类,并且通过拷贝这些原型,创建新的对象。,允许一个对象再创建另外一个可定制的对象, 无需知道如何创建的细节。
原理:通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝它们自己来实施创建,即:对象.clone()
- 改进代码
- Sheep类
package com.yqj.pattern.prototype.improve;
public class Sheep implements Cloneable {
private String name;
private int age;
private String color;
public Sheep(String name, int age, String color) {
this.name = name;
this.age = age;
this.color = color;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
@Override
public String toString() {
return "Sheep{" +
"name='" + name + '\'' +
", age=" + age +
", color='" + color + '\'' +
'}';
}
@Override
protected Object clone() {
Object sheep = null;
try {
sheep = super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return sheep;
}
}
- Client类
package com.yqj.pattern.prototype.improve;
public class Client {
public static void main(String[] args) {
Sheep sheep = new Sheep("tom", 1, "白色");
Sheep sheep2 = (Sheep) sheep.clone();
Sheep sheep3 = (Sheep) sheep.clone();
System.out.println(sheep+" hashcode:"+sheep.hashCode());
System.out.println(sheep2+" hashcode:"+sheep2.hashCode());
System.out.println(sheep3+" hashcode:"+sheep3.hashCode());
}
}
- 深拷贝和浅拷贝
- 浅拷贝
对于数据类型是基本数据类型的成员变量,浅拷贝会直接进行值传递,也就是将该属性值复制一份给新的对象。
对于数据类型是引用数据类型的成员变量,比如说成员变量是某个数组、某个类 的对象等,那么浅拷贝会进行引用传递,也就是只是将该成员变量的引用值(内 存地址)复制一份给新的对象。因为实际上两个对象的该成员变量都指向同一个 实例。在这种情况下,在一个对象中修改该成员变量会影响到另一个对象的该成 员变量值
浅拷贝是使用默认的 clone()方法来实现 sheep = (Sheep) super.clone();
- 深拷贝
复制对象的所有基本数据类型的成员变量值
为所有引用数据类型的成员变量申请存储空间,并复制每个引用数据类型成员变 量所引用的对象,直到该对象可达的所有对象。也就是说,对象进行深拷贝要对 整个对象进行拷贝
深拷贝实现方式1:重写clone方法来实现深拷贝
深拷贝实现方式2:通过对象序列化实现深拷贝(推荐)
建造者模式
- 案例
盖房子项目:
- 需要建房子:这一过程为打桩、砌墙、封顶 。
- 房子有各种各样的,比如普通房,高楼,别墅,各种房子的过程虽然一样,但是要求不要相同的。
- 传统方法
package com.yqj.pattern.builder.traditional;
//抽象房子类
abstract class AbstractHouse {
public abstract void buildBasic();
public abstract void buildWalls();
public abstract void roofed();
public void build(){
buildBasic();
buildWalls();
roofed();
}
}
//普通房子
class CommonHouse extends AbstractHouse{
@Override
public void buildBasic() {
System.out.println("普通房子打地基");
}
@Override
public void buildWalls() {
System.out.println("普通房子刷墙");
}
@Override
public void roofed() {
System.out.println("普通房子封顶");
}
}
//别墅
class HighBuilding extends AbstractHouse{
@Override
public void buildBasic() {
System.out.println("别墅打地基");
}
@Override
public void buildWalls() {
System.out.println("别墅刷墙");
}
@Override
public void roofed() {
System.out.println("别墅封顶");
}
}
//用户
public class Client{
public static void main(String[] args) {
CommonHouse commonHouse = new CommonHouse();
commonHouse.build();
HighBuilding highBuilding = new HighBuilding();
highBuilding.build();
}
}
- 分析
设计的程序结构,过于简单,没有设计缓存层对象,程序的扩展和维护不好. 也就 是说,这种设计方案,把产品(即:房子) 和 创建产品的过程(即:建房子流程) 封装在一起,耦合性增强了。应该想办法将产品和过程分离,达到解耦的目的。
- 建造者模式
解释:它可以将复杂对象的建造过程抽象出来(抽象类别),使这个抽象过程的不同实现方法可以构造出不同表现(属性)的对象。
建造者模式的四个角色:
- Product(产品角色): 一个具体的产品对象。
- Builder(抽象建造者): 创建一个Product对象的各个部件指定的 接口/抽象类。
- ConcreteBuilder(具体建造者): 实现接口,构建和装配各个部件。
- Director(指挥者): 构建一个使用Builder接口的对象。它主要是用于创建一个 复杂的对象。它主要有两个作用,一是:隔离了客户与对象的生产过程,二是: 负责控制产品对象的生产过程。
- 改进方法
package com.yqj.pattern.builder.improve;
//产品
class House{
private String basic;
private String wall;
private String roofed;
public String getBasic() {
return basic;
}
public void setBasic(String basic) {
this.basic = basic;
}
public String getWall() {
return wall;
}
public void setWall(String wall) {
this.wall = wall;
}
public String getRoofed() {
return roofed;
}
public void setRoofed(String roofed) {
this.roofed = roofed;
}
@Override
public String toString() {
return "House{" +
"basic='" + basic + '\'' +
", wall='" + wall + '\'' +
", roofed='" + roofed + '\'' +
'}';
}
}
//抽象建造者
abstract class HouseBuilder{
protected House house = new House();
//建造流程,定义有关的抽象方法
public abstract void buildBasic();
public abstract void buildWalls();
public abstract void roofed();
//建造好房子,返回产品
public House buildHouse(){
return house;
}
}
//具体建造者1
class CommonHouseBuilder extends HouseBuilder{
//实现具体的构建方法
@Override
public void buildBasic() {
house.setBasic("普通房子打地基");
}
@Override
public void buildWalls() {
house.setWall("普通房子刷墙");
}
@Override
public void roofed() {
house.setRoofed("普通房子封顶");
}
}
//具体建造者2
class HighBuilding extends HouseBuilder{
@Override
public void buildBasic() {
house.setBasic("别墅打地基");
}
@Override
public void buildWalls() {
house.setWall("别墅刷墙");
}
@Override
public void roofed() {
house.setRoofed("别墅封顶");
}
}
//指挥者
class HouseDirector{
HouseBuilder houseBuilder;
public HouseDirector(HouseBuilder houseBuilder) {
this.houseBuilder = houseBuilder;
}
public void setHouseBuilder(HouseBuilder houseBuilder) {
this.houseBuilder = houseBuilder;
}
//制定产品的构建流程
public House build(){
houseBuilder.buildBasic();
houseBuilder.buildWalls();
houseBuilder.roofed();
//返回产品
return houseBuilder.buildHouse();
}
}
//用户
public class Client{
public static void main(String[] args) {
HouseDirector director = new HouseDirector(new CommonHouseBuilder());
System.out.println(director.build());
//更改指挥者的构建产品
director.setHouseBuilder(new HighBuilding());
System.out.println(director.build());
}
}
- 小结
- 客户端(使用程序)不必知道产品内部组成的细节,将产品本身与产品的创建过程解 耦,使得相同的创建过程可以创建不同的产品对象。
- 每一个具体建造者都相对独立,而与其他的具体建造者无关,因此可以很方便地替 换具体建造者或增加新的具体建造者, 用户使用不同的具体建造者即可得到不同 的产品对象。
- 可以更加精细地控制产品的创建过程 。将复杂产品的创建步骤分解在不同的方法 中,使得创建过程更加清晰,也更方便使用程序来控制创建过程。
- 增加新的具体建造者无须修改原有类库的代码,指挥者类针对抽象建造者类编程, 系统扩展方便,符合 “开闭原则”
- 建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制。
- 如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大,因此在这种情况下,要考虑是否选择建造者模式。
- 抽象工厂模式VS建造者模式 抽象工厂模式实现对产品家族的创建,一个产品家族是这样的一系列产品:具有不同分类维度的产品组合,采用抽象工厂模式不需要关心构建过程,只关心什么产品由什么工厂生产即可。而建造者模式则是要求按照指定的蓝图建造产品,它的主要目的是通过组装零配件而产生一个新产品。