原型模式
原型模式(Prototype Pattern)是指原型实例指定创建对象的种类,并且通过拷贝这些这些原型创建新的对象,属于创建型模式。(对不通过new关键字,而是通过对象拷贝来实现创建对象的模式称为原型模式)。
原型模式的核心在于拷贝原型对象。以系统中已经存在的一个对象为原型,直接基于内存二进制流进行拷贝,无需再经历耗时的对象初始化过程(不调用构造函数)。
原型模式适用以下场景:
- 类初始化消耗资源比较多
- new产生的一个对象需要非常繁琐的过程
- 构造函数比较复杂
- 循环体中产生大量的对象
一个标准的原型模式代码:
interface IPrototype<T>{T clone();
}class ConcretePrototype implements IPrototype{private int age;private String name;public int getAge() {return age;}public void setAge(int age) {this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}@Overridepublic ConcretePrototype clone() {ConcretePrototype concretePrototype = new ConcretePrototype();concretePrototype.setAge(this.age);concretePrototype.setName(this.name);return concretePrototype;}@Overridepublic String toString() {return "ConcretePrototype{" +"age=" + age +", name='" + name + '\'' +'}';}
}public class Test {public static void main(String[] args) {ConcretePrototype prototype = new ConcretePrototype();prototype.setAge(18);prototype.setName("Tom");System.out.println(prototype);ConcretePrototype cloneType = prototype.clone();System.out.println(cloneType);}
}
上面的复制过程是我们自己完成的,另外,JDK已经帮我们实现了一个现成的API,我们只需要实现Cloneable接口即可。
class ConcretePrototype implements Cloneable{private int age;private String name;public int getAge() {return age;}public void setAge(int age) {this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}@Overridepublic ConcretePrototype clone() {try {return (ConcretePrototype) super.clone();} catch (CloneNotSupportedException e) {e.printStackTrace();return null;}}@Overridepublic String toString() {return "ConcretePrototype{" +"age=" + age +", name='" + name + '\'' +'}';}
}
然而,复制后的克隆对象是的成员复制的不是值,而是引用的地址。如果我们修改任意一个对象中的属性值,prototype和cloneType的属性值都会改变。这就是我们常说的浅克隆。只是完整复制了值数据类型,没有赋值引用对象(所有的引用对象仍然指向原来的对象)。
使用序列化实现深度克隆:
class ConcretePrototype implements Cloneable, Serializable {private int age;private String name;private List<String> hobbies;public int getAge() {return age;}public void setAge(int age) {this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public List<String> getHobbies() {return hobbies;}public void setHobbies(List<String> hobbies) {this.hobbies = hobbies;}@Overridepublic ConcretePrototype clone() {try {return (ConcretePrototype) super.clone();} catch (CloneNotSupportedException e) {e.printStackTrace();return null;}}public ConcretePrototype deepClone(){try{ByteArrayOutputStream bos = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(bos);oos.writeObject(this);ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());ObjectInputStream ois = new ObjectInputStream(bis);return (ConcretePrototype) ois.readObject();}catch (Exception e){e.printStackTrace();return null;}}@Overridepublic String toString() {return "ConcretePrototype{" +"age=" + age +", name='" + name + '\'' +", hobbies=" + hobbies +'}';}
}public class Test {public static void main(String[] args) {ConcretePrototype prototype = new ConcretePrototype();prototype.setAge(18);prototype.setName("Tom");List<String> hobbies = new ArrayList<>();hobbies.add("Math");hobbies.add("baseball");prototype.setHobbies(hobbies);System.out.println(prototype);ConcretePrototype cloneType = prototype.deepClone();System.out.println(cloneType);System.out.println(prototype.getHobbies());System.out.println(cloneType.getHobbies());System.out.println(prototype.getHobbies() == cloneType.getHobbies());}
}
克隆破坏单例模式
如果我们克隆的目标对象是单例对象,那么深克隆就会破坏单例。为了防止克隆破坏单例,只要禁止深克隆就行。
一种是不实现Cloneable接口,重新clone方法,在clone方法中返回单例对象即可。
原型模式的优缺点:
优点:
1、性能优良,Java自带的原型模式是基于内存二进制流的拷贝,比直接new一个对象性能提升许多。
2、可以使用深克隆方式保存对象的状态,使用原型模式将对象复制一份并将其保存起来,简化了创建对象的过程。
缺点:
1、需要为每一个类配置一个克隆方法。
2、克隆方法位于类的内部,当对已有的类进行改造的时候,需要修改代码,违反了开闭原则。
3、实现深度克隆需要编写较为复杂的代码。
建造者模式
建造者模式(Builder Pattern)是将一个复杂对象的构建过程与它的表示分离,使得同样的构建过程可以创建不同的表示,属于创建型模式。建造者模式适用于创建对象需要很多步骤,但步骤的顺序不一定固定。如果一个对象有非常复杂的内部结构(很多属性),可以将复杂对象的创建和使用进行分离。
建造者模式的基本写法:
import lombok.Data;@Data
class Course{private String name;private String ppt;private String video;
} class CourseBuilder{private Course course = new Course();public void addName(String name){course.setName(name);}public void addPpt(String ppt){course.setPpt(ppt);}public void addVideo(String video){course.setVideo(video);}public Course build(){return this.course;}
}public class Test {public static void main(String[] args) {CourseBuilder builder = new CourseBuilder();builder.addName("设计模式");builder.addPpt("【ppt】");builder.addVideo("【视频】");Course course = builder.build();System.out.println(course);}
}
建造者模式的链式写法:
import lombok.Data;class CourseBuilder{@Datapublic class Course{private String name;private String ppt;private String video;}private Course course = new Course();public CourseBuilder addName(String name){course.setName(name);return this;}public CourseBuilder addPpt(String ppt){course.setPpt(ppt);return this;}public CourseBuilder addVideo(String video){course.setVideo(video);return this;}public Course build(){return this.course;}
}public class Test {public static void main(String[] args) {CourseBuilder builder = new CourseBuilder().addName("设计模式").addPpt("【ppt】").addVideo("【视频】");System.out.println(builder.build());}
}
在JDK中StringBuilder,它提供append()方法,就是应用了建造者模式。
建造者模式的优缺点:
优点:
1、封装性好,创建和使用分离;
2、扩展性好,建造类之间独立,一定程度上解耦。
缺点:
1、产生多余的Builder对象
2、产品内部发生变化,建造者都要修改,成本比较大。
建造者模式和工厂模式的区别:
1、建造者模式更加注重方法的调用顺序,工厂模式注重于创建对象。
2、创建对象的力度不同,建造者模式创建复杂的对象,由各种复杂的部件组成,工厂模式创建出来的都一样。
3、关注点不一样,工厂模式只需要把对象创建出来就可以了,而建造者模式不仅要创建这个对象,还要知道这个对象由哪些部件组成。
4、建造者模式根据建造过程中的顺序不一样,最终的对象部件组成也不一样。