设计模式------原型模式与建造者模式
目录
1、原型模式
1.1 什么是原型模式
原型模式(Prototype Pattern):使用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。原型模式是一种对象创建型模式。
原型模式的工作原理很简单:将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝自己来实现创建过程。
原型模式是一种“另类”的创建型模式,创建克隆对象的工厂就是原型类自身,工厂方法由克隆方法来实现。
需要注意的是通过克隆方法所创建的对象是全新的对象,它们在内存中拥有新的地址,通常对克隆所产生的对象进行修改对原型对象不会造成任何影响,每一个克隆对象都是相互独立的。通过不同的方式修改可以得到一系列相似但不完全相同的对象。
1.2 结构图
- 原型模式共有以下几种角色:
- Prototype(抽象原型类)定义clone方法的接口,类型为接口、抽象类或者具体的类,是所有具体原型类的父类
- ConcretePrototype(具体原型类)实现clone方法的实现了,可以为多个类,提供不同的clone方式
- Client(客户类)让一个原型对象复制新的对象
- 原型方法的核心是如何实现clone方法,不同的clone方法最终的效果也是不一样的
1.3 浅克隆与深克隆实例
浅拷贝:当对象被复制时,只复制对象本身与值类型的成员变量,引用类型的成员变量没有被复制
深拷贝:当对象被复制时,对象本身、值类型成员变量、引用类型成员变量都会被复制,原型对象与复制对象完全独立
区别:浅克隆和深克隆的主要区别在于是否支持引用类型的成员变量的复制
package com.zps.framerlearn.设计模式.原型对象;
import lombok.Data;
import java.io.*;
import java.util.List;
/**
* @description: 原型对象
* @author: zps
* @create: 2020-05-02 16:57
**/
@Data
public class Student implements Cloneable , Serializable{
private String name;
private int age;
private List<String> hobbby;
//浅克隆
public Student clone(){
try {
return (Student) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return null;
}
//深克隆
public Student deepClone() throws IOException, ClassNotFoundException {
//将对象写入流中
ByteArrayOutputStream bao = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bao);
oos.writeObject(this);
//将对象从流中取出
ByteArrayInputStream bis = new ByteArrayInputStream(bao.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return ((Student)ois.readObject());
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", hobbby=" + hobbby +
'}';
}
}
测试
package com.zps.framerlearn.设计模式.原型对象;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* @description: 测试原型模式
* @author: zps
* @create: 2020-05-02 17:01
**/
public class Test {
public static void main(String args[]) throws IOException, ClassNotFoundException {
Student stu = new Student();
stu.setName("张三");
stu.setAge(21);
List<String> hobhy = new ArrayList<>();
hobhy.add("看书");
hobhy.add("写字");
stu.setHobbby(hobhy);
System.out.println(stu);
Student student = stu.deepClone();
student.getHobbby().add("游泳");
System.out.println(student);
System.out.println(stu);
}
}
深克隆测试运行结果
Student{name='张三', age=21, hobbby=[看书, 写字]}
Student{name='张三', age=21, hobbby=[看书, 写字, 游泳]}
Student{name='张三', age=21, hobbby=[看书, 写字]}
实现深克隆主要有两种方式:
(1)通过序列化的方式:
public Student deepClone() throws IOException, ClassNotFoundException { //将对象写入流中 ByteArrayOutputStream bao = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bao); oos.writeObject(this); //将对象从流中取出 ByteArrayInputStream bis = new ByteArrayInputStream(bao.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bis); return ((Student)ois.readObject()); }
(2)手动对引用对象进行硬编码克隆:
@Override protected Object clone() throws CloneNotSupportedException { Student stu = (Studetn)super.clone(); //深克隆 stu.hobby = (Student) stu.hobby.clone(); return stu; }
1.4 典型应用
(1)ArrayList
对 clone
的重写如下:
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
//省略
public Object clone() {
try {
ArrayList<?> v = (ArrayList<?>) super.clone();
v.elementData = Arrays.copyOf(elementData, size);
v.modCount = 0;
return v;
} catch (CloneNotSupportedException e) {
// this shouldn't happen, since we are Cloneable
throw new InternalError(e);
}
}
//...省略
}
(2)HashMap中的应用
public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable {
@Override
public Object clone() {
HashMap<K,V> result;
try {
result = (HashMap<K,V>)super.clone();
} catch (CloneNotSupportedException e) {
// this shouldn't happen, since we are Cloneable
throw new InternalError(e);
}
result.reinitialize();
result.putMapEntries(this, false);
return result;
}
// ...省略...
}
1.5 小总结
原型模式的主要优点如下:
- 当创建新的对象实例较为复杂时,使用原型模式可以简化对象的创建过程,通过复制一个已有实例可以提高新实例的创建效率
- 可以使用深克隆的方式保存对象的状态,使用原型模式将对象复制一份并将其状态保存起来,以便在需要的时候使用(如恢复到某一历史状态),可辅助实现撤销操作。
原型模式的主要缺点如下:
- 当对已有的类进行改造时,需要修改源代码,违背了“开闭原则”。
- 在实现深克隆时需要编写较为复杂的代码,而且当对象之间存在多重的嵌套引用时,为了实现深克隆,每一层对象对应的类都必须支持深克隆,实现起来可能会比较麻烦
2、构建者模式
2.1 什么是构建者模式
建造者模式(Builder Pattern):将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。建造者模式是一种对象创建型模式。
建造者模式一步一步创建一个复杂的对象,它允许用户只通过指定复杂对象的类型和内容就可以构建它们,用户不需要知道内部的具体构建细节。
2.2 结构图
- 建造者模式主要包含如下几个角色:
- Builder(抽象建造者):构建产品对象部件方法的抽象接口,可以是接口也可以是抽象类,供指挥者使用;方法包含两类bulidX()与getResult()方法
- ConcreteBuilder(具体建造者):实现抽象建造者接口,一个具体建造者对应一中具体的产品对象
- Product(产品角色):建造者模式最终产物
- Director(指挥者):又称为导演类,它负责安排产品对象的各个部件的建造顺序
- 抽象建造者提供抽象建造部件方法,具体建造者实现这些抽象方法,指挥者调用这些抽象方法,完成构建同样构建过程但是不同实现的对象的建造
- 建造者模式与抽象工厂模式的区别是前者是建造一个完整的复杂产品,而后者是一系列产品,前者像汽车组装厂,后者像汽车配件生产厂
2.3 实例演示
import lombok.Data; /** * @description: 产品:课程 * @author: zps * @create: 2020-05-02 19:18 **/ @Data public class Course { private String name; private String ppt; private String video; private String note; private String homework; @Override public String toString() { return "Course{" + "name='" + name + '\'' + ", ppt='" + ppt + '\'' + ", video='" + video + '\'' + ", note='" + note + '\'' + ", homework='" + homework + '\'' + '}'; } }
/** * @description: 课程建造者 * @author: zps * @create: 2020-05-02 19:20 **/ public class CourseBuilder { private Course course = new Course(); public void addName(String name){ course.setName(name); } public void addPpt(String ppt){ course.setName(ppt); } public void addVideo(String video){ course.setName(video); } public void addNote(String note){ course.setName(note); } public void addHomework(String homework){ course.setName(homework); } public Course builder(){ return course; } }
/** * @description: 测试 * @author: zps * @create: 2020-05-02 19:24 **/ public class Test { public static void main(String args[]){ CourseBuilder builder = new CourseBuilder(); builder.addName("喝了酒"); builder.addNote("yes"); System.out.println(builder.builder()); } }
可以改造成为链接方式。
/** * @description: 课程建造者 * @author: zps * @create: 2020-05-02 19:20 **/ public class CourseBuilder { private Course course = new Course(); public CourseBuilder addName(String name){ course.setName(name); return this; } public CourseBuilder addPpt(String ppt){ course.setName(ppt); return this; } public CourseBuilder addVideo(String video){ course.setName(video); return this; } public CourseBuilder addNote(String note){ course.setName(note); return this; } public CourseBuilder addHomework(String homework){ course.setName(homework); return this; } public Course builder(){ return course; } }
2.4 典型应用
(1)java.lang.StringBuilder 中的建造者模式
Appendable
接口如下
public interface Appendable {
Appendable append(CharSequence csq) throws IOException;
Appendable append(CharSequence csq, int start, int end) throws IOException;
Appendable append(char c) throws IOException;
}
StringBuilder
中的 append
方法使用了建造者模式,不过装配方法只有一个,并不算复杂,append
方法返回的是 StringBuilder
自身,即采用了连接方法。
public final class StringBuilder extends AbstractStringBuilder implements java.io.Serializable, CharSequence {
@Override
public StringBuilder append(String str) {
super.append(str);
return this;
}
// ...省略...
}
StringBuilder
的父类 AbstractStringBuilder
实现了 Appendable
接口
abstract class AbstractStringBuilder implements Appendable, CharSequence {
char[] value;
int count;
public AbstractStringBuilder append(String str) {
if (str == null)
return appendNull();
int len = str.length();
ensureCapacityInternal(count + len);
str.getChars(0, len, value, count);
count += len;
return this;
}
private void ensureCapacityInternal(int minimumCapacity) {
// overflow-conscious code
if (minimumCapacity - value.length > 0) {
value = Arrays.copyOf(value,
newCapacity(minimumCapacity));
}
}
// ...省略...
}
我们可以看出,Appendable 为抽象建造者,定义了建造方法,StringBuilder 既充当指挥者角色,又充当产品角色,又充当具体建造者,建造方法的实现由 AbstractStringBuilder 完成,而 StringBuilder 继承了 AbstractStringBuilder
注意:StringBuilder 和 StringBuffer 的主要区别是后者是线程安全的。
(2)mybatis 中 mybatis 中的建造者模式
2.5 小总结
建造者模式的优点:
- 在建造者模式中,客户端不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象。
- 每一个具体建造者都相对独立,而与其他的具体建造者无关,因此可以很方便地替换具体建造者或增加新的具体建造者,用户使用不同的具体建造者即可得到不同的产品对象。由于指挥者类针对抽象建造者编程,增加新的具体建造者无须修改原有类库的代码,系统扩展方便,符合 “开闭原则”。
建造者模式的缺点:
- 如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大,增加系统的理解难度和运行成本。
- 建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,例如很多组成部分都不相同,不适合使用建造者模式,因此其使用范围受到一定的限制。
建造者模式与工厂模式的区别:
- 建造者模式更加注重方法的调用顺序,工厂模式注重与创建对象
- 关注点:工厂模式只需要把对象创建出来就可以了,而建造者模式不仅需要创建出这个对象,还要知道这个对象