zoukankan      html  css  js  c++  java
  • 【设计模式】建造者模式

    建造者模式的理解

      定义: 官方的说法是,将一个复杂的对象的构建与它的表示分离,即隐藏了复杂对象的创建过程,把复杂对象的创建过程加以抽象,通过子类继承或者重载的方式,动态的创建具有复合属性的对象。

     

    四个角色:

       1.产品角色(Product):最终要生成的对象实例

       2.抽象建造者(Builder):构建者的抽象基类(也可用接口代替),里面定义了构建product的步骤。通常还包含一个返回复杂产品的方法 getResult()。

       3.具体建造者(Concrete Builder):实现 Builder 接口,完成复杂产品的各个部件的具体创建方法。

       4.指挥者(Director):它调用建造者对象中的部件构造与装配方法完成复杂对象的创建,在指挥者中不涉及具体产品的信息。

     

    具体使用

       1.首先生成一个director

       2.然后生成一个目标builder

       3.接着使用director组装builder

       4.组装完毕后使用builder创建产品实例

     

    大白话理解:

       比如现在有5个产品,他们的参数全部或者大部分是固定的,比如颜色、大小、重量等,那么我们就需要写死在一个地方,这就可以用到建造者模式,这样每次直接创建就可以按照如下顺序直接创建出来了。先创建director、再创建builder、接着使用director组装builder、组装完毕后使用builder创建产品实例

     

    什么时候可以使用

       1.当一个类里面的属性过多的时候,建议使用

       2.当一个类的构造函数参数个数超过4个,而且这些参数有些是可选的参数,考虑使用构造者模式

     

    建造者模式主要适用于以下应用场景:

       1.相同的方法,不同的执行顺序,产生不同的结果。

       2.多个部件或零件,都可以装配到一个对象中,但是产生的结果又不相同。

       3.产品类非常复杂,或者产品类中不同的调用顺序产生不同的作用。

       4.初始化一个对象特别复杂,参数多,而且很多参数都具有默认值。

     

    优点

       1.使用建造者模式可以使客户端不必知道产品内部组成的细节。

       2.具体的建造者类之间是相互独立的,这有利于系统的扩展。

       3.具体的建造者相互独立,因此可以对建造的过程逐步细化,而不会对其他模块产生任何影响。

     

    缺点

       1.建造者模式所创建的产品一般具有较多的共同点,其组成部分相似;如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制。

       2.如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大。

       3.如果产品内部发生变化,则建造者也要同步修改,后期维护成本较大。

     

    建造者模式和工厂模式的区别

       1.建造者模式更加注重方法的调用顺序,工厂模式注重创建对象。

       2.创建对象的力度不同,建造者模式创建复杂的对象,由各种复杂的部件组成,工厂模式创建出来的对象都一样

       3.关注重点不一样,工厂模式只需要把对象创建出来就可以了,而建造者模式不仅要创建出对象,还要知道对象由哪些部件组成。

       4.建造者模式根据建造过程中的顺序不一样,最终对象部件组成也不一样。

     

    传统方式的建造者模式

    背景介绍

    这里我是参考了我们项目里的一个业务,简单说就是有一个任务表,里面可以存储多种类型的任务,不同类型的任务存储的字段以及值不同。里面共有20多个字段。现在暂定只有两种任务。打印任务,即打印pdf文件的任务。刻录任务,即刻录光盘的任务。

    具体实现

    1. 创建一个任务的实体类,这个就很常见了,里面定义一些属性和方法

    2. 创建一个抽象建造者类,里面定义好多个抽象的方法,将来会将这些方法组合起来组成建造的流程。

    3. 创建两个具体的建造者类,一个是打印任务建造者,一个是刻录任务建造者,分别继承抽象建造者,并重写抽象方法。

    4. 创建一个指挥者类,里面可以定义通过构造器或者setter方法传入参数,最后定义一个具体的建造任务的流程方法。

    5. 最后创建一个测试类,可以通过传入不同的具体建造者类,来创建出不同的任务类。

    import lombok.Data;
    
    //首先定义一个Product产品类
    //定义一些属性和方法
    @Data
    public class Task {
    
        //任务类别  1:打印  2:刻录
        private Integer type;
    
        //任务名称
        private String name;
    
        //使用状态 1:使用 0:禁用
        private Integer status;
    
        //省略其他字段
    
        //这里可以定义一些Task类的方法,随便举个例子,比如获取task任务的信息
        public void getTaskMsg(){
            System.out.println("当前任务的信息为:Type:"+type+" Name:"+name+" Status"+status);
        }
    
    }
    
    //定义一个抽象建造者类
    //里面可以自定义多个抽象方法,即这些抽象的方法组合起来就是建造的流程
    public abstract class TaskBuilder {
    
        protected Task task = new Task();
    
        //抽象方法-->建造的流程
        public abstract void buildType();
    
        public abstract void buildName();
    
        public abstract void buildStatus();
    
        //建造任务,然后将产品(任务)return
        public Task buildTask() {
            return task;
        }
    
    }
    
    //接下来新建两个具体的建造者对象,分别代表打印任务和刻录任务,并让他们继承抽象继承者
    public class PrintTask extends TaskBuilder {
    
        //这里可以传参,也可以不传。具体逻辑自己定义,返回值也可以自己定义。
        //可以通过返回具体建造者类来实现链式调用。
        @Override
        public void buildType() {
            task.setType(1);
        }
    
        @Override
        public void buildName() {
            task.setName("打印任务");
        }
    
        @Override
        public void buildStatus() {
            task.setStatus(1);
        }
    }
    
    //具体含义可以参考PrintTask建造者
    public class BurnTask extends TaskBuilder {
        @Override
        public void buildType() {
            task.setType(2);
        }
    
        @Override
        public void buildName() {
            task.setName("刻录任务");
        }
    
        @Override
        public void buildStatus() {
            task.setStatus(1);
        }
    }
    
    //指挥者,在这里可以动态的去指定制作流程,返回产品
    public class TaskDirector {
    
        TaskBuilder taskBuilder = null;
        //下面可以看出,可以通过构造器或者setter方式实现赋值
        //构造器传入 taskBuilder
        public TaskDirector(TaskBuilder taskBuilder) {
            this.taskBuilder = taskBuilder;
        }
        //通过setter传入taskBuilder
        public void setTaskBuilder(TaskBuilder taskBuilder) {
            this.taskBuilder = taskBuilder;
        }
        //如何处理建造任务的流程,交给指挥者。
        public Task createTask() {
            taskBuilder.buildName();
            taskBuilder.buildStatus();
            taskBuilder.buildType();
            return taskBuilder.buildTask();
        }
    
    }
    
    //测试类
    public class Test {
    
        public static void main(String[] args) {
            //创建打印任务
            TaskDirector taskDirector1 = new TaskDirector(new PrintTask());
            Task printTask = taskDirector1.createTask();
            System.out.println(printTask);
    
            //创建刻录任务
            TaskDirector taskDirector2 = new TaskDirector(new BurnTask());
            Task burnTask = taskDirector2.createTask();
            System.out.println(burnTask);
        }
    
    }
    View Code

    结果:

    //Task(type=1, name=打印任务, status=1)
    //Task(type=2, name=刻录任务, status=1)

     

    简化后的建造者模式

    步骤如下:

    1. 创建一个user类,里面定义三个属性用于演示,实际上user表可能有超过20个字段的可能。

    2. 创建一个建造者类,里面将user类改称为其的内部类,并将各个属性的构造步骤添加进去,每次完成一个步骤都返回this。

    3. 创建一个测试类,使用建造者方式的链式写法和传统方式两种方法分别创建user对象,并设置属性值,可以看到建造者方法,代码更简洁,逻辑更清楚,且user对象的属性越多,优点越明显。

    //定义一个user对象,并定义三个属性,类上加上Data注解,省略了set get方法
    @Data
    public class User {
        private String name;
    
        private Integer age;
    
        private String sex;
    }
    
    //创建建造者类
    public class UserBuilder {
        //将User类改成为UserBuilder类的内部类,并将构造步骤添加进来,每次完成一个步骤都返回this
        private User user = new User();
    
        public UserBuilder addName(String name) {
            this.user.setName(name);
            return this;
        }
    
        public UserBuilder addAge(Integer age) {
            this.user.setAge(age);
            return this;
        }
    
        public UserBuilder addSex(String sex) {
            this.user.setSex(sex);
            return this;
        }
    
        public User builder() {
            return this.user;
        }
    }
    
    //创建测试类
    public class Test {
        public static void main(String[] args) {
            //下面使用了两种方法来创建了user对象,那么可以看出方法1使用了建造者模式的链式写法,
            //代码更简洁,逻辑更清楚。且user对象的属性越多,优点越明显
    
            //方法1
            UserBuilder userBuilder = new UserBuilder();
            userBuilder.addAge(11).addName("张三").addSex("男");
            User user = userBuilder.builder();
            System.out.println("建造者方法生成用户为:"+user);
    
            //方法2
            User user2 = new User();
            user2.setSex("女");
            user2.setName("小丽");
            user2.setAge(11);
            System.out.println("普通方法生成对象:"+user2);
        }
    }

     

    应用

      我们在开发过程中常见的StringBuilder其实就用到了建造者模式,并使用了链式调用,如下:

    public static void main(String[] args) {
        StringBuilder builder = new StringBuilder();
        builder.append("1").append("2").append("3");
        System.out.println(builder);
    }

    分析

      首先看他的部分源码

      总结:

        1.Appendable接口定义了多个append方法(抽象方法),即Appendable为抽象建造者,定义了抽象方法。

        2.AbstractStringBuilder实现了Appendable接口。所以他是建造者,虽然他是一个抽象类,不能实例化。

        3.StringBuilder类继承AbstractStringBuilder,对于它来说,它既充当了指挥者角色,同时充当了具体的建造者。建造方法的具体实现是由AbstractStringBuilder完成

    //StringBuilder
    public final class StringBuilder extends AbstractStringBuilder implements java.io.Serializable, CharSequence{
    
      static final long serialVersionUID = 4383685877147921099L;
    
      @Override
      public StringBuilder append(Object obj) {
        return append(String.valueOf(obj));
      }
    
      @Override
      public StringBuilder append(String str) {
        super.append(str);
        return this;
      }
    
      public java.lang.StringBuilder append(StringBuffer sb) {
        super.append(sb);
        return this;
      }
    
      @Override
      public java.lang.StringBuilder append(CharSequence s) {
        super.append(s);
        return this;
      }
    
    }
    
    //AbstractStringBuilder
    abstract class AbstractStringBuilder implements Appendable, CharSequence {}
    
    
    //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;
    }
    View Code

     

    参考:

    https://www.jianshu.com/p/3d1c9ffb0a28
    https://zhuanlan.zhihu.com/p/58093669
    http://c.biancheng.net/view/1354.html
    https://blog.csdn.net/Woo_home/article/details/104362776
    https://blog.csdn.net/qq_42339210/article/details/106742211

     

     

  • 相关阅读:
    【JAVA笔记——道】JAVA对象销毁
    【JAVA笔记——道】并发编程CAS算法
    httpClientUtil的get请求
    python基础 day11 下 ORM介绍 sqlalchemy安装 sqlalchemy基本使用 多外键关联 多对多关系 表结构设计作业
    python基础 day11 上 数据库介绍 mysql 数据库安装使用 mysql管理 mysql 数据类型 常用mysql命令 事务 索引 python 操作mysql ORM sqlachemy学习
    Python基础 Day10 Gevent协程 SelectPollEpoll异步IO与事件驱动 Python连接Mysql数据库操作 RabbitMQ队列 RedisMemcached缓存 Paramiko SSH Twsited网络框架
    python基础 day9 进程、与线程区别 python GIL全局解释器锁 线程 进程
    python基础 day8 Socket语法及相关 SocketServer实现多并发
    python基础 day7 面向对象高级语法部分 异常处理 异常处理 Socket开发基础
    python基础 day6 面向对象的特性:封装、继承、多态 类、方法、
  • 原文地址:https://www.cnblogs.com/flyinghome/p/15196047.html
Copyright © 2011-2022 走看看