设计模式:03-原型模式 / 建造者模式(生成器模式)

本篇博客主要是学习 韩顺平_Java设计模式 做一个学习笔记使用
5. 原型模式
基本介绍
1) 原型模式(Prototype 模式)是指:用原型实例指定创建对象的种类,并且通过拷贝这些原型,创建新的对象
2) 原型模式是一种创建型设计模式,允许一个对象再创建另外一个可定制的对象,无需知道如何创建的细节
3) 工作原理是:通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝它 们自己来实施创建,即 对象.clone()
4) 形象的理解:孙大圣拔出猴毛, 变出其它孙大圣
5.1. 问题的引入
/**
* @author houyu
* @createTime 2019/11/18 20:38
*/
public class Demo1 {
/**
* 定义个类 Person
*/
public static class Person {
private int age;
private String name;
public Person(int age, String name) {
this.age = age;
this.name = name;
}
public int getAge() {
return age;
}
public String getName() {
return name;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("Person{");
sb.append("age=").append(age);
sb.append(", name='").append(name).append('\'');
sb.append('}');
return sb.toString();
}
}
public static void main(String[] args) {
// 问题的引入:
// 需要拷贝 Person 5份
Person original = new Person(10, "tom");
//
Person person1 = new Person(original.getAge(), original.getName());
Person person2 = new Person(original.getAge(), original.getName());
Person person3 = new Person(original.getAge(), original.getName());
Person person4 = new Person(original.getAge(), original.getName());
Person person5 = new Person(original.getAge(), original.getName());
//
System.out.println(person1);
System.out.println(person2);
System.out.println(person3);
System.out.println(person4);
System.out.println(person5);
/**
* 分析问题:
* 传统的方式的优缺点
*
* 1) 优点是比较好理解,简单易操作。
* 2) 在创建新的对象时,总是需要重新获取原始对象的属性,如果创建的对象比较复杂时,效率较低
* 3) 总是需要重新初始化对象,而不是动态地获得对象运行时的状态, 不够灵活
* 4) 改进的思路分析
*/
}
}
5.2 原型模式的浅拷贝
使用默认的原型模式实现的是对象的浅拷贝
什么是浅拷贝?
这个对象的类属性, 如果是基本类型 boolean int long double float char byte short 等类似会进行值传递, 如果是String(final) 也会> 进行值传递
如果是引用类型, 那就就引用拷贝, 比如 List Map Array(int[]…)
- 实现接口 implements Cloneable
- 重写方法: @Override protected Object clone(){}
- 调用端调用clone()即可
代码
import java.util.Arrays;
/**
* @author houyu
* @createTime 2019/11/18 20:38
*/
public class Demo2 {
/**
* 定义个类 Person
*/
public static class Person implements Cloneable {
private int age;
private String name;
private int[] array = {1, 2};
public Person(int age, String name) {
this.age = age;
this.name = name;
}
public int getAge() {
return age;
}
public String getName() {
return name;
}
public int[] getArray() {
return array;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("Person{");
sb.append("age=").append(age);
sb.append(", name='").append(name).append('\'');
sb.append(", array=").append(Arrays.toString(array));
sb.append('}');
return sb.toString();
}
@Override
protected Object clone() {
try {
return super.clone();
} catch(CloneNotSupportedException e) {
e.printStackTrace();
}
return null;
}
}
public static void main(String[] args) {
// 问题的引入:
// 需要拷贝 Person 5份
Person original = new Person(10, "tom");
//
Person person1 = (Person) original.clone();
Person person2 = (Person) original.clone();
Person person3 = (Person) original.clone();
Person person4 = (Person) original.clone();
Person person5 = (Person) original.clone();
//
System.out.println(person1 == original);
System.out.println(person2 == original);
System.out.println(person3.getArray() == original.getArray());
System.out.println(person4);
System.out.println(person5);
/*
* false
* false
* true
* Person{age=10, name='tom', array=[1, 2]}
* Person{age=10, name='tom', array=[1, 2]}
*/
//
/**
* 注意点:
*
* 1. 使用默认的原型模式实现的是对象的浅拷贝
*
* 什么是浅拷贝?
* 这个对象的类属性, 如果是基本类型 boolean int long double float char byte short 等类似会进行值传递, 如果是String(final) 也会进行值传递
*
* 如果是引用类型, 那就就引用拷贝, 比如 List Map Array(int[]...)
*
*/
}
}
5.3 重写默认原型模式实现深拷贝
注意点:
- 使用默认的原型模式close,会导致引用类型没有深拷贝,引用是同一个,因此可能会导致其他问题,如:线程安全等。。。
- 改进方案1(不推荐):在原来的clone基础之上,手动一个一个添加引用类型的clone
- 改进方案2(推荐):使用流写入写出进行克隆(ObjectOutputStream,ObjectInputStream)
- 改进方案3(推荐):使用JSON序列化工具进行克隆(fastjson,gson,jackson…)
- 实现接口 implements Cloneable, Serializable
- 重写方法: @Override protected Object clone(){},使用 ObjectOutputStream ObjectInputStream 实现
- 调用端调用clone()即可
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Arrays;
/**
* @author houyu
* @createTime 2019/11/18 20:38
*/
public class Demo3 {
/**
* 定义个类 Person
*/
public static class Person implements Cloneable, Serializable {
private int age;
private String name;
private int[] array = {1, 2};
public Person(int age, String name) {
this.age = age;
this.name = name;
}
public int getAge() {
return age;
}
public String getName() {
return name;
}
public int[] getArray() {
return array;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("Person{");
sb.append("age=").append(age);
sb.append(", name='").append(name).append('\'');
sb.append(", array=").append(Arrays.toString(array));
sb.append('}');
return sb.toString();
}
@Override
protected Object clone() {
ObjectOutputStream objectOutputStream = null;
ByteArrayOutputStream byteArrayOutputStream = null;
ObjectInputStream objectInputStream = null;
ByteArrayInputStream byteArrayInputStream = null;
try {
byteArrayOutputStream = new ByteArrayOutputStream();
objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(this);
byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
objectInputStream = new ObjectInputStream(byteArrayInputStream);
return objectInputStream.readObject();
} catch(IOException | ClassNotFoundException e) {
e.printStackTrace();
} finally {
close(objectOutputStream, byteArrayOutputStream, objectInputStream, byteArrayInputStream);
}
return null;
}
private void close(Closeable... closeables) {
for(Closeable closeable : closeables) {
if(closeable == null) {
continue;
}
try { closeable.close(); } catch(IOException e) {}
}
}
}
public static void main(String[] args) {
// 问题的引入:
// 需要拷贝 Person 5份
Person original = new Person(10, "tom");
//
Person person1 = (Person) original.clone();
Person person2 = (Person) original.clone();
Person person3 = (Person) original.clone();
Person person4 = (Person) original.clone();
Person person5 = (Person) original.clone();
//
System.out.println(person1 == original);
System.out.println(person2 == original);
System.out.println(person3.getArray() == original.getArray());
System.out.println(person4);
System.out.println(person5);
/*
* false
* false
* true
* Person{age=10, name='tom', array=[1, 2]}
* Person{age=10, name='tom', array=[1, 2]}
*/
//
/**
* 注意点:
*
* 1. 必须需要实现Serializable
*
*/
}
}
5.4 原型模式在 Spring 框架中源码分析(getBean)
// 1.0 org.springframework.context.support.AbstractApplicationContext
public Object getBean(String name) throws BeansException {
assertBeanFactoryActive();
return getBeanFactory().getBean(name);
}
// 2.0 org.springframework.beans.factory.support.AbstractBeanFactory
public Object getBean(String name) throws BeansException {
return doGetBean(name, null, null, false);
}
// 3.0 org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean()
// Create bean instance.
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// Explicitly remove instance from singleton cache: It might have been put there
// eagerly by the creation process, to allow for circular reference resolution.
// Also remove any beans that received a temporary reference to the bean.
destroySingleton(beanName);
throw ex;
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
else if (mbd.isPrototype()) {
// It's a prototype -> create a new instance.
Object prototypeInstance = null;
try {
beforePrototypeCreation(beanName);
prototypeInstance = createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}
else {
String scopeName = mbd.getScope();
final Scope scope = this.scopes.get(scopeName);
if (scope == null) {
throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
}
try {
Object scopedInstance = scope.get(beanName, () -> {
beforePrototypeCreation(beanName);
try {
return createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
});
bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
}
catch (IllegalStateException ex) {
throw new BeanCreationException(beanName,
"Scope '" + scopeName + "' is not active for the current thread; consider " +
"defining a scoped proxy for this bean if you intend to refer to it from a singleton",
ex);
}
}
6. 建造者模式(生成器模式)
基本介绍
1) 建造者模式(Builder Pattern) 又叫生成器模式,是一种对象构建模式。它可以将复杂对象的建造过程抽象出 来(抽象类别),使这个抽象过程的不同实现方法可以构造出不同表现(属性)的对象。
2) 建造者模式 是一步一步创建一个复杂的对象,它允许用户只通过指定复杂对象的类型和内容就可以构建它们, 用户不需要知道内部的具体构建细节。
6.1 建造者模式的四个角色
1) Product(产品角色): 一个具体的产品对象。
2) Builder(抽象建造者): 创建一个 Product 对象的各个部件指定的 接口/抽象类。
3) ConcreteBuilder(具体建造者): 实现接口,构建和装配各个部件。
4) Director(指挥者): 构建一个使用 Builder 接口的对象。它主要是用于创建一个复杂的对象。它主要有两个作 用,一是:隔离了客户与对象的生产过程,二是:负责控制产品对象的生产过程。
建造者模式解决盖房需求应用实例
/**
* @author houyu
* @createTime 2019/11/18 20:00
*/
public class Demo1 {
/**
* 产品: 房屋
*/
public static class House {
private String wall;
private String houseTop;
public String getWall() {
return wall;
}
public House setWall(String wall) {
this.wall = wall;
return this;
}
public String getHouseTop() {
return houseTop;
}
public House setHouseTop(String houseTop) {
this.houseTop = houseTop;
return this;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("House{");
sb.append(", wall='").append(wall).append('\'');
sb.append(", houseTop='").append(houseTop).append('\'');
sb.append('}');
return sb.toString();
}
}
/**
* 抽象建造者: 房屋抽象构建者
*
* interface / abstract class
*/
public interface HouseBuilder {
/**
* 做墙
*/
void makeWall();
/**
* 做房顶
*/
void makeHourTop();
/**
* 制作完成房子
*/
House buildHouse();
}
/**
* 具体构建者: 我的房屋构建者
*/
public static class MyHouseBuilder extends House implements HouseBuilder {
House house = new House();
@Override
public void makeWall() {
house.setWall("MyHouseBuilder.makeWall");
}
@Override
public void makeHourTop() {
house.setHouseTop("MyHouseBuilder.makeHourTop");
}
@Override
public House buildHouse() {
/**
* 由 具体构建者 进行构建
*/
makeWall();
makeHourTop();
return house;
}
}
/**
* 指挥者: 我自己
*/
public static class My {
private HouseBuilder builder;
public My setBuilder(HouseBuilder builder) {
this.builder = builder;
return this;
}
public House build() {
return builder.buildHouse();
}
}
public static void main(String[] args) {
// 创建具体构建者
HouseBuilder builder = new MyHouseBuilder();
// 创建指挥者
My my = new My();
// 给指挥者传递构建者
my.setBuilder(builder);
// 指挥者调用构建者的具体流程构建完成返回房子
House build = my.build();
// 输出房子
System.out.println("build.toString() = " + build.toString());
/**
* 这个案例的构建者模式是比较标准的, 很明确的存在4个角色
*
* 在很多源码中的构建者模式对于这4个角色, 分界比较模糊, 有些类担任双重角色也是有可能的, 如StringBuilder
* 也有些构建者模式没有完全存在这4个角色, 有的只有三个角色也有可能的...
*/
}
}
说明:
在很多源码中的构建者模式对于这4个角色, 分界比较模糊, 有些类担任双重角色也是有可能的, 如StringBuilder
也有些构建者模式没有完全存在这4个角色, 有的只有三个角色也有可能的…
6.2 构造者模式在 JDK 框架中源码分析(StringBuilder)
// 产品
public final class String implements java.io.Serializable, Comparable<String>, CharSequence, Constable, ConstantDesc {
}
// 抽象构建者
public interface Appendable {
// ...
Appendable append(CharSequence csq) throws IOException;
}
// 具体建造者 (AbstractStringBuilder 实现了 Appendable 接口方法,这里的 AbstractStringBuilder 已经是建造者,只是不能 实例化)
abstract class AbstractStringBuilder implements Appendable, CharSequence {
// ...
public AbstractStringBuilder append(Object obj) {
return append(String.valueOf(obj));
}
}
// StringBuilder 即充当了指挥者角色,同时充当了具体的建造者,建造方法的实现是由 AbstractStringBuilder 完成, 而 StringBuilder 继承了 AbstractStringBuilder
public final class StringBuilder extends AbstractStringBuilder implements java.io.Serializable, Comparable<StringBuilder>, CharSequence {
// ...
public StringBuilder(String str) {
super(str.length() + 16);
append(str);
}
}
6.3 构造者模式在 lombok 中的应用
6.3.1编写实体类
import lombok.Builder;
@Builder
public class Friend {
private String name;
private Integer age;
}
6.3.2编写使用类
public class Test {
public static void main(String[] args) throws Exception {
// 下面一行代码充当了 指挥者 和 具体建造者
Friend friend = Friend.builder().age(10).name("张三").build();
System.out.println("friend = " + friend);
}
}
6.3.3反编译 Friend 的构建者
public class Friend {
private String name;
private Integer age;
Friend(String name, Integer age) {
this.name = name;
this.age = age;
}
public static Friend.FriendBuilder builder() {
return new Friend.FriendBuilder();
}
// 这里没有抽象构建者, 只有具体构建者, 因此构建者模式不一定说一定要存在4个角色的
public static class FriendBuilder {
private String name;
private Integer age;
FriendBuilder() {
}
public Friend.FriendBuilder name(String name) {
this.name = name;
return this;
}
public Friend.FriendBuilder age(Integer age) {
this.age = age;
return this;
}
public Friend build() {
return new Friend(this.name, this.age);
}
public String toString() {
return "Friend.FriendBuilder(name=" + this.name + ", age=" + this.age + ")";
}
}
}
6.4 建造者模式的注意事项和细节
1) 客户端(使用程序)不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可 以创建不同的产品对象
2) 每一个具体建造者都相对独立,而与其他的具体建造者无关,因此可以很方便地替换具体建造者或增加新的具 体建造者, 用户使用不同的具体建造者即可得到不同的产品对象
3) 可以更加精细地控制产品的创建过程 。将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰, 也更方便使用程序来控制创建过程
4) 增加新的具体建造者无须修改原有类库的代码,指挥者类针对抽象建造者类编程,系统扩展方便,符合“开闭 原则”
5) 建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,则不适合使 用建造者模式,因此其使用范围受到一定的限制。
6) 如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大,因 此在这种情况下,要考虑是否选择建造者模式.
7) 抽象工厂模式 VS 建造者模式
抽象工厂模式实现对产品家族的创建,一个产品家族是这样的一系列产品:具有不同分类维度的产品组合,采 用抽象工厂模式不需要关心构建过程,只关心什么产品由什么工厂生产即可。而建造者模式则是要求按照指定 的蓝图建造产品,它的主要目的是通过组装零配件而产生一个新产品