设计模式4,建造者模式、原型模式

目录

建造者模式

原型模式


建造者模式

将一个复杂对象的构建与他的表示分离,使得同样的构建过程可以创建不同的表示。用户只需指定需要建造的类型,不需要知道建造过程及细节。

建造者模式和工厂模式的区别在于,建造者模式在乎方法的调用顺序、可以创造各种不同的由不同的复杂部件组成的产品,工厂模式注重创建产品、创建出的产品都一个样子。总的来说就是,当一个对象创建需要很多步骤时,适合建造者模式,只需要一个简单的方法就可以创建的,用工厂模式。

类型:创建型

使用场景:一个有非常复杂的内部结构(许多属性)的对象,或是想把复杂对象的创建和使用分开。

优点:封装性好,建造和使用分离。扩展型好,建造类之间独立,在一定程度上解耦。

缺点:会产生多余的Builder对象,产品内部发生变化的时候,建造者都需要修改,成本较大。

例子:

public class Software {
    private String name;
    private String type;
    private Double price;
    private String describe;
    private String size;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public Double getPrice() {
        return price;
    }

    public void setPrice(Double price) {
        this.price = price;
    }

    public String getDescribe() {
        return describe;
    }

    public void setDescribe(String describe) {
        this.describe = describe;
    }

    public String getSize() {
        return size;
    }

    public void setSize(String size) {
        this.size = size;
    }

    @Override
    public String toString() {
        return "Software{" +
                "name='" + name + '\'' +
                ", type='" + type + '\'' +
                ", price=" + price +
                ", describe='" + describe + '\'' +
                ", size='" + size + '\'' +
                '}';
    }
}
public interface SoftwareBuilder {
    void buildSoftwareName(String name);
    void buildSoftwareType(String type);
    void buildSoftwarePrice(Double price);
    void buildSoftwareDescribe(String describe);
    void buildSoftwareSize(String size);

    Software releaseSoftware();
}
public class SoftwareActualBuilder implements SoftwareBuilder {
    private Software software = new Software();

    @Override
    public void buildSoftwareName(String name) {
        software.setName(name);
    }

    @Override
    public void buildSoftwareType(String type) {
        software.setType(type);
    }

    @Override
    public void buildSoftwarePrice(Double price) {
        software.setPrice(price);
    }

    @Override
    public void buildSoftwareDescribe(String describe) {
        software.setDescribe(describe);
    }

    @Override
    public void buildSoftwareSize(String size) {
        software.setSize(size);
    }

    @Override
    public Software releaseSoftware() {
        return software;
    }
}
public class Salesman {
    private SoftwareBuilder softwareBuilder;

    public void setSoftwareBuilder(SoftwareBuilder softwareBuilder) {
        this.softwareBuilder = softwareBuilder;
    }

    public Software releaseSoftware(String name , String type,
                                    Double price , String describe,
                                    String size){
        this.softwareBuilder.buildSoftwareName(name);
        this.softwareBuilder.buildSoftwareType(type);
        this.softwareBuilder.buildSoftwarePrice(price);
        this.softwareBuilder.buildSoftwareDescribe(describe);
        this.softwareBuilder.buildSoftwareSize(size);
        return this.softwareBuilder.releaseSoftware();
    }
}
public class Test {
    public static void main(String[] args) {
        SoftwareBuilder softwareBuilder = new SoftwareActualBuilder();
        Salesman salesman = new Salesman();
        salesman.setSoftwareBuilder(softwareBuilder);
        Software software = salesman.releaseSoftware("魔兽世界",
                "游戏",
                75.00,
                "很好玩",
                "60G"
                );
        System.out.println(software);
    }
}

Test通过Salesman调用抽象Builder,通过传入具体Builder,Salesman返回来创建对象,不需要和具体对象打交道,这样如果有好几种商品,可以通过创建不同的Builder来实现实体的调用。

当然,上面的建造者模式结构看起来很麻烦,也跟常见的建造者模式不一样,一般来说,建造者模式的builder都是直接创建,接下来优化代码:

public class Software {
    private String name;
    private String type;
    private Double price;
    private String describe;
    private String size;

    public Software(SoftwareBuilder softwareBuilder) {
        this.name = softwareBuilder.name;
        this.type = softwareBuilder.type;
        this.price = softwareBuilder.price;
        this.describe = softwareBuilder.describe;
        this.size = softwareBuilder.size;
    }

    public static class SoftwareBuilder{
        private String name;
        private String type;
        private Double price;
        private String describe;
        private String size;

        public SoftwareBuilder buildSoftwareName(String name){
            this.name = name;
            return this;
        }

        public SoftwareBuilder buildSoftwareType(String type) {
            this.type = type;
            return this;
        }

        public SoftwareBuilder buildSoftwarePrice(Double price) {
            this.price = price;
            return this;
        }

        public SoftwareBuilder buildSoftwareDescribe(String describe) {
            this.describe = describe;
            return this;
        }

        public SoftwareBuilder buildSoftwareSize(String size) {
            this.size = size;
            return this;
        }

        public Software build(){
            return new Software(this);
        }
    }

    @Override
    public String toString() {
        return "Software{" +
                "name='" + name + '\'' +
                ", type='" + type + '\'' +
                ", price=" + price +
                ", describe='" + describe + '\'' +
                ", size='" + size + '\'' +
                '}';
    }
}
public class Test {
    public static void main(String[] args) {
        Software software = new Software.SoftwareBuilder()
                .buildSoftwareName("魔兽世界")
                .buildSoftwareType("游戏")
                .buildSoftwarePrice(75.00)
                .buildSoftwareDescribe("很好玩")
                .buildSoftwareSize("60G")
                .build();
        System.out.println(software);
    }
}

 

用到设计模式的源码:

最典型的就是StringBuilder和StringBuffer

从这也可以看出,StringBuilder和StringBuffer的区别,也只是Buffer是同步的。

再比如:SqlSessionFactoryBuilder

里面有一堆build方法,需要注意下面这个,建造者中嵌套建造者,这也是以后编码可以参考的地方。


原型模式

原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象。他不需要知道任何创建的细节,且不调用构造函数。

类型:创建型

使用场景:类初始化消耗资源过多的时候。new产生一个新的对象需要非常繁琐的过程(数据准备、访问权限等)。构造函数比较复杂的时候。循环体中生产大量对象时。

优点:性能比直接new一个对象性能高。简化创建过程。

缺点:必须配备(覆盖)克隆方法。对克隆复杂对象或克隆出的对象进行复杂改造时,容易引入风险。深拷贝、浅拷贝要运用得当。

例子:

原型模式在系统中是以二进制流的形式拷贝对象的,要比直接new一个对象性能会好很多。

想要实现克隆,实现Cloneable就可以了

public class Email implements Cloneable{
    private String title;
    private String address;
    private String content;

    public Email() {
        System.out.println("Email Class Constructor");
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        System.out.println("clone Email Object");
        return super.clone();
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    @Override
    public String toString() {
        return "Email{" +
                "title='" + title + '\'' +
                ", address='" + address + '\'' +
                ", content='" + content + '\'' +
                '}';
    }
}
public class EmailUtil {
    public static void sendEmail(Email email){
        String outputContent = "主题:{0},邮件地址:{1},邮件内容:{2},发送成功!";
        System.out.println(MessageFormat.format(outputContent, email.getTitle()
            ,email.getAddress() , email.getContent()));
    }

    public static void saveOriginRecord(Email email){
        System.out.println("存储originMail记录《" + email.getContent() + "》");
    }
}
public class Test {
    public static void main(String[] args) throws CloneNotSupportedException {
        Email email = new Email();
        email.setContent("初始化模板");

        for (int i = 0 ; i < 5 ; i ++){
            Email mailTemp = (Email) email.clone();
            mailTemp.setTitle("标题" + i);
            mailTemp.setAddress("address" + i + "@haozi.com");
            mailTemp.setContent("这是邮件内容");
            EmailUtil.sendEmail(mailTemp);
        }
        EmailUtil.saveOriginRecord(email);
    }
}

运行结果:

类图就不贴了,很简单,这里说一下深克隆和浅克隆,这个非常重要!

浅克隆:

public class Haozi implements Cloneable {
    private String name;
    private Date birthday;

    public Haozi(String name, Date birthday) {
        this.name = name;
        this.birthday = birthday;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    @Override
    public String toString() {
        return "Haozi{" +
                "name='" + name + '\'' +
                ", birthday=" + birthday +
                '}' + super.toString();
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }
}
public class Test {
    public static void main(String[] args) throws CloneNotSupportedException {
        Date birthday = new Date(0L);
        Haozi haozi1 = new Haozi("耗子" , birthday);
        Haozi haozi2 = (Haozi) haozi1.clone();

        System.out.println(haozi1);
        System.out.println(haozi2);

        haozi1.getBirthday().setTime(6666666666666L);

        System.out.println(haozi1);
        System.out.println(haozi2);
    }
}

这里是为了显示某些内容,所以故意写的跟平常不一样。运行结果:

可以看出,前面我们只是调整了haozi1的日期,但是haozi2的跟这变了,通过debug可以看到,haozi2克隆了haozi1,但两个的Date对象是同一个。

 深克隆,需要对需要深克隆的对象单独写克隆方法:

@Override
    protected Object clone() throws CloneNotSupportedException {
        Haozi haozi = (Haozi) super.clone();
        haozi.birthday = (Date) haozi.birthday.clone();
        return haozi;
    }

这样,引用的date对象就是两个了。

 用到设计模式的源码:

源于源码,ArrayList、HashMap等集合都实现了Cloneable接口,重写了clone方法,这里就不贴了,具体的自己去看。

原型模式在设计中运用非常广发,在日常使用中一定要注意深克隆和浅克隆!!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值