→23种设计模式大纲
一、建造者模式
定义
将一个对象的构建和表示分离开,使得同样的构建过程可以创建不同的表示
角色
- 产品 Product :被构造的对象
- 抽象建造者 Builder :提供被构造对象需要 构造的部分 的方法定义
- 具体建造者 ConcreteBuilder:实现抽象建造者接口,负责产品的 各个部分的设置,不负责组装
- 引导者 Director :持有建造者引用,负责指定产品以何种顺序构建
基础实现
建造者
//抽象建造者
public interface Builder {
void part1(String part1);
void part2(String part2);
void part3(String part3);
Production build();
}
//具体建造者
class ConcreteBuilder implements Builder{
Production production=new Production();
@Override
public void part1(String part1) {
production.setPart1(part1);
}
@Override
public void part2(String part2) {
production.setPart2(part2);
}
@Override
public void part3(String part3) {
production.setPart3(part3);
}
@Override
public Production build(){
return this.production;
}
}
引导者、产品
public class Director {
private Builder builder;
public Director(Builder builder){
this.builder=builder;
}
public void construct(){
builder.part1("asd1");
builder.part2("asd2");
builder.part3("asd3");
}
}
@Data
@NoArgsConstructor
@AllArgsConstructor
class Production {
private String part1;
private String part2;
private String part3;
}
使用
Builder builder=new ConcreteBuilder();
Director director = new Director(builder);
director.construct();
Production production = builder.build();
总结:
- 建造者负责每个部分的组装,不参与整体的构造;
- 引导者持有建造者的引用;
- 建造者内部持有唯一的一个产品;
这种基础的建造者模式在实际使用中,并不多见。另外的变种使用更多。
变种
public class Product {
private String name;
private String password;
private String id;
public Product(ProductBuilder productBuilder) {
this.name= productBuilder.name;
this.password= productBuilder.password;
this.id= productBuilder.id;
}
public static ProductBuilder builder() {
return new ProductBuilder();
}
public static class ProductBuilder {
private String name;
private String password;
private String id;
public ProductBuilder name(String name) {
this.name = name;
return this;
}
public ProductBuilder password(String password) {
this.password = password;
return this;
}
public ProductBuilder id(String id) {
this.id = id;
return this;
}
public Product build(){
return new Product(this);
}
}
}
像lombok也提供了@Builder注解,将该注解置于实体类上,也可以使用这种变种的建造者模式。
总结一下变种建造者模式的特点:
- 为实体赋值的操作都发生在Builder内部;
- 每次赋值完参数后,都返回Builder本身,使得可以不断的链式赋值;
- 调用builder的build方法时,通过Product的构造方法,完整的构造出Product对象;
二、原型模式
原型模式也叫做克隆模式。
创建一个对象时开销较大,可以拷贝一个已创建的实例,新创建的实例拥有原实例的所有属性。避免出现创建实例时的开销问题。
实现
java实现较为简单:
- 拷贝的实体实现 Cloneable 接口
- 拷贝时调用实例的 clone() 方法
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class User implements Cloneable{
private String name;
private String password;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public class Test {
public static void main(String[] args) throws CloneNotSupportedException {
//使用了Lombok的@Builder注解,所以可以链式赋值
User user = User.builder().name("asd").password("asdsad").build();
User clone = (User) user.clone();
}
}
浅拷贝和深拷贝
前面说过通过拷贝出来的对象和原实例有一样的属性。但是也需要分属性来看的。
浅拷贝:只拷贝原对象。
深拷贝:将原对象内部的其他对象一起拷贝。
例如在User内加入Address的成员变量:
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class User implements Cloneable{
private String name;
private String password;
private Address address;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
@Data
@NoArgsConstructor
@AllArgsConstructor
class Address implements Cloneable{
private String id;
private String area;
private String city;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
测试内容
public class Test {
public static void main(String[] args) throws CloneNotSupportedException {
Address address = new Address("四川");
User user = User.builder().name("asd").password("asdsad").address(address).build();
User clone = (User) user.clone();
System.out.println("user " + user);
System.out.println("clone " + clone);
address.setArea("北京");
System.out.println("改变原user中address后 --> user " + user);
System.out.println("改变原user中address后 --> clone " + clone);
}
}
执行结果
user User(name=asd, password=asdsad, address=Address(area=四川))
clone User(name=asd, password=asdsad, address=Address(area=四川))
改变原user中address后 --> user User(name=asd, password=asdsad, address=Address(area=北京))
改变原user中address后 --> clone User(name=asd, password=asdsad, address=Address(area=北京))
分析一下:新的 user 和原来的 user 持有相同的address引用。所以在修改address内容后,两个user的address对象都发生了改变了。也就是说 内部的 Address 并没有被拷贝。
所以此时就需要实现深拷贝了,实现深拷贝一般有两种方法:
- 原对象和其内部的所有其他对象都实现 Cloneable接口,再在实体内部统一处理;
- 使用序列化来实现深拷贝。需要对象实现Serializable 接口。
这里提一下第一种方式:(还需要Address类实现Cloneable)
这样新拷贝的对象就是深拷贝对象。
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class User implements Cloneable{
private String name;
private String password;
private Address address;
@Override
protected Object clone() throws CloneNotSupportedException {
//统一做特殊处理
User clone = (User) super.clone();
clone.setAddress((Address) this.getAddress().clone());
return clone;
}
}