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

      我们还是举上一节的例子:生产汽车。上一节我们通过模板方法模式控制汽车跑起来的动作,那么需求是无止境的,现在如果老板又增加了额外的需求:汽车启动、停止、鸣笛引擎声都由客户自己控制,他想要什么顺序就什么顺序,那该如何做呢?

    1. 汽车无休止的改造

            假如现在要生产两种车,奔驰和宝马,这两辆车都有共性,我们所需要关注的是单个车的运行过程,这才是老板所关心的点所在。我们先这样想,针对这个需求,我们要找到一个切入点,那就是产品类,每个车都是一个产品,那么在产品类中我们可以控制车的运行顺序,这样每个车都可以拥有自己想要的顺序了。基于此,我们设计如下类图:

             我们看到CarModel中有个setSequence方法,通过传入一个ArrayList来控制运行顺序,run方法根据这个ArrayList中保存的顺序执行,然后奔驰车和宝马车分别继承这个CarModel即可,这貌似是很好的实现,它很像上一节的模板方法模式,只是多了个方法可以设置运行顺序。我们看一下CarModel具体代码的实现:

    public abstract class CarModel {  
          
        private ArrayList<String> sequence = new ArrayList<String>(); //维护一个ArrayList保存执行命令关键字  
          
        protected abstract void start();  
        protected abstract void stop();  
        protected abstract void alarm();  
        protected abstract void engineBoom();  
          
        final public void run() {  
            for(int i = 0; i < this.sequence.size(); i ++) { //根据ArrayList中保存的顺序执行相应的动作  
                String actionName = this.sequence.get(i);  
                if(actionName.equalsIgnoreCase("start")) {  
                    this.start(); //启动汽车  
                } else if(actionName.equalsIgnoreCase("stop")) {  
                    this.stop(); //停止汽车  
                } else if(actionName.equalsIgnoreCase("alarm")) {  
                    this.alarm(); //汽车鸣笛  
                } else if(actionName.equalsIgnoreCase("engine boom")) {  
                    this.engineBoom(); //汽车轰鸣  
                }  
            }  
        }  
          
        final public void setSequence(ArrayList<String> sequence) { //获得执行顺序的命令,即一个ArrayList  
            this.sequence = sequence;  
        }  
    }  

     CarModel中的setSequence方法允许客户自己设置一个顺序,我们看看子类的实现:

    public class BenzModel extends CarModel {  
      
        @Override  
        protected void start() {  
            System.out.println("奔驰启动……");  
        }  
      
        @Override  
        protected void stop() {  
            System.out.println("奔驰停止……");  
        }  
      
        @Override  
        protected void alarm() {  
            System.out.println("奔驰鸣笛……");  
        }  
      
        @Override  
        protected void engineBoom() {  
            System.out.println("奔驰轰鸣");  
        }  
      
    }  
    //宝马就略了……一样的  

       下面我们增加一个测试类实现该需求:

    public class Client {  
      
        public static void main(String[] args) {  
            BenzModel benz = new BenzModel();  
            //存放run顺序  
            ArrayList<String> sequence = new ArrayList<String>();  
            sequence.add("engine boom"); //老板说:跑之前先轰鸣比较帅!  
            sequence.add("start");  
            sequence.add("stop");  
            //我们把这个顺序赋予奔驰  
            benz.setSequence(sequence);  
            benz.run();  
        }  
    }  

      这样好像已经顺利完成了任务了,但是别忘了,我们这只是满足了一个需求,如果下一个需求是宝马车只轰鸣,再下一个需求是奔驰车只跑不停……等等……那岂不是要一个个写测试类来实现?显然这不是我们想要的。

            我们可以这样做:为每种产品模型定义一个建造者,你要啥顺序直接告诉建造者,由建造者来建造即可,于是我们重新设计类图:

            我们增加了一个CarBuilder类,由它来组装各个车模型,要什么类型的顺序就由相关的子类去完成即可,我们来看看CarBuilder的代码:

    public abstract class CarBuilder {  
        //建造一个模型,你要给我一个顺序要求  
        public abstract void setSequence(ArrayList<String> sequence);  
        //设置完毕顺序后,就可以直接拿到这个车辆模型了  
        public abstract CarModel getCarModel();  
    }  

        很简单,每个车辆模型都要有确定的运行顺序,然后才能返回一个车辆模型,奔驰车和宝马车组装者的代码如下:

    public class BenzBuilder extends CarBuilder {  
      
        private BenzModel benz = new BenzModel(); //奔驰车模型  
          
        @Override  
        public void setSequence(ArrayList<String> sequence) {  
            this.benz.setSequence(sequence); //设置奔驰车模型的运行顺序  
        }  
      
        @Override  
        public CarModel getCarModel() {  
            return this.benz; //将这个模型返回  
        }  
    }  
    //宝马车一样,不写了……  

    现在两辆车的组装者都写好了,现在我们写一个测试类来测试一下:

    public class Client {  
      
        public static void main(String[] args) {  
      
            //存放run顺序  
            ArrayList<String> sequence = new ArrayList<String>();  
            sequence.add("engine boom");  
            sequence.add("start");  
            sequence.add("stop");  
            //要用这个顺序造一辆奔驰  
            BenzBuilder benzBuilder = new BenzBuilder();  
            //把顺序给奔驰组装者  
            benzBuilder.setSequence(sequence);  
            //奔驰组装者拿到顺序后就给你生产一辆来  
            BenzModel benz = (BenzModel) benzBuilder.getCarModel();  
            benz.run();  
        }  
      
    }  

     如果我要生产一辆宝马车,只需要换成宝马车的组装者即可,这样我们不用直接访问产品类了,全部访问组装者就行,是不是感觉到很方便,我管你怎么生产,我扔给你个顺序,你给我弄辆车出来,要的就是这种效果!

            可是人的需求是个无底洞,特别是老板,他哪天不爽了,又要换顺序,这样还是挺麻烦的,四个过程(start、stop、alarm、engine boom)按排列组合也有很多中情况,我们不能保证老板想要哪种顺序,咋整?无奈,我们只能使出最后的杀手锏了,找个设计师过来指挥各个时间的先后顺序,然后为每种顺序指定一个代码,你说一种我们立刻就给你生产!我们再修改一下类图……

            类图看着有点复杂,其实不然,只是在原来的基础上增加了一个Director类充当着设计师的角色,负责按照指定的顺序生产模型,比如我们要一个A顺序的奔驰车,B顺序的奔驰车,A顺序的宝马车,B顺序的宝马车……等等,我们来看下Director类的代码:

    public class Director {  
        private ArrayList<String> sequence = new ArrayList<String>();  
        private BenzBuilder benzBuilder = new BenzBuilder();  
        private BWMBuilder bwmBuilder = new BWMBuilder();  
          
        //A顺序的奔驰车  
        public BenzModel getABenzModel() {  
            this.sequence.clear();  
            this.sequence.add("start");  
            this.sequence.add("stop");  
            //返回A顺序的奔驰车  
            this.benzBuilder.setSequence(sequence);  
            return (BenzModel) this.benzBuilder.getCarModel();  
        }  
          
        //B顺序的奔驰车  
        public BenzModel getBBenzModel() {  
            this.sequence.clear();  
            this.sequence.add("engine boom");  
            this.sequence.add("start");  
            this.sequence.add("stop");  
            //返回B顺序的奔驰车  
            this.benzBuilder.setSequence(sequence);  
            return (BenzModel) this.benzBuilder.getCarModel();  
        }  
          
        //C顺序的宝马车  
        public BenzModel getCBWMModel() {  
            this.sequence.clear();  
            this.sequence.add("start");  
            this.sequence.add("alarm");  
            this.sequence.add("stop");  
            //返回C顺序的宝马车  
            this.bwmBuilder.setSequence(sequence);  
            return (BenzModel) this.bwmBuilder.getCarModel();  
        }  
      
        //D顺序的宝马车  
        public BenzModel getDBWMModel() {  
            this.sequence.clear();  
            this.sequence.add("engine boom");  
            this.sequence.add("start");  
            //返回D顺序的宝马车  
            this.bwmBuilder.setSequence(sequence);  
            return (BenzModel) this.bwmBuilder.getCarModel();  
        }  
          
        //还有很多其他需求,设计师嘛,想啥需求就给你弄啥需求  
    }  

        有了这样一个设计师,我们的测试类就更容易处理了,比如现在老板要10000辆A类奔驰车,100000辆B类奔驰车,20000C类型宝马车,D类型不要:

    public class Client {  
      
        public static void main(String[] args) {  
      
            Director director = new Director();  
              
            for(int i = 0; i < 10000; i ++) {  
                director.getABenzModel();  
            }  
              
            for(int i = 0; i < 100000; i ++) {  
                director.getBBenzModel();  
            }  
              
            for(int i = 0; i < 20000; i ++) {  
                director.getCBWMModel();  
            }  
        }  
      
    }  

      是不是很清晰很简单,我们重构代码的最终第就是简单清晰。这就是建造者模式。

    2. 建造者模式的定义

            我们来看看建造者模式的一般定义:Separate the construction of a complex object from its representation so that the same construction process can create different representations. 即:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。比如上面的例子,我们可以用同样的顺序构造不同的车。建造者模式的通用类图如下:

            Product是最终的产品类,Builder是建造者,Director是指挥者。Director负责安排已有模块的顺序,然后告诉Builder开始建造。

    3. 建造者模式的优点

            1)封装性:使用建造者模式可以是客户端不必知道产品内部组成的细节。

            2)建造者独立,容易扩展:BenzBuilder和BMWBuilder是相互独立的,对系统扩展非常有利。

            3)便于控制细节风险:由于具体的建造者是独立的,因此可以对建造者过程逐步细化,而不对其他的模块产生任何影响。

    4. 建造者模式的使用场景

            1)相同的方法,不同的执行顺序,产生不同的事件结果时,可以使用建造者模式。

            2)多个部件或零件,都可以装配到一个对象中,但是产生的运行结果又不想同时,可以使用建造者模式。

            3)产品类非常复杂,或者产品类中的调用顺序不同产生了不同的效能,这时候可以使用建造者模式。

            4)在对象创建过程中会使用到系统的一些其他对象,这些对象在产品对象的创建过程中不易得到,也可以采用建造者模式封装该对象的创建过程。这种场景只能是一个补偿的方法,因为一个对象不容易获得,而在设计阶段竟然没有发现,而要通过设计这模式来柔化创建过程,本身设计已经出问题了。

            到这里,我们会发现,建造者模式和工厂方法模式有点像。但是两者有区别:建造者模式关注的是零件类型和装配工艺(顺序),而工厂模式是创建一个对象,这是最大不同的地方。

            创建者模式就介绍这么多吧,如有错误之处,欢迎留言指正~

  • 相关阅读:
    导包路径
    django导入环境变量 Please specify Django project root directory
    替换django的user模型,mysql迁移表报错 django.db.migrations.exceptions.InconsistentMigrationHistory: Migration admin.0001_initial is applied before its dependen cy user.0001_initial on database 'default'.
    解决Chrome调试(debugger)
    check the manual that corresponds to your MySQL server version for the right syntax to use near 'order) values ('徐小波','XuXiaoB','男','1',' at line 1")
    MySQL命令(其三)
    MySQL操作命令(其二)
    MySQL命令(其一)
    [POJ2559]Largest Rectangle in a Histogram (栈)
    [HDU4864]Task (贪心)
  • 原文地址:https://www.cnblogs.com/shanheyongmu/p/6374275.html
Copyright © 2011-2022 走看看