zoukankan      html  css  js  c++  java
  • Java设计模式----建造者模式

     创建一个对象,有时候是很复杂的一件事,比如在模板方法模式中讲到的Car对象,创建它,就需要装配底盘、轮胎、发动机、车身等部件,甚至之后还需要喷漆等工序。模版方法模式,主要解决的是算法中不变部分与可变部分的解耦,将可变部分算法推迟到子类中去实现;而本文要介绍的建造者模式,则是一种创建模式,顾名思义,就是为了创建对象的设计模式。


    1.建造者模式

     建造者模式(Builder Pattern),将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的对象。

    所谓对象的构建与它的表示分离,就是创建一个对象的实例和成员变量的赋值相分离,因此,不同的建造者子类根据不同的赋值就可以创建出不同的对象。

    实际上一个复杂对象就是一些“部件”的聚合体,建造者模式中,通常需要一个“指挥者”Director来组织管理对每一部分的构建,以组合成一个完整的对象,再将这个对象返回给客户。

    建造者模式的UML类图如下所示:

    Builder : 建造者抽象父类或者接口,定义创建对象各个“部件”的接口;

    ConcreteBuilder: 建造者的具体实现类,可以有不同实现,不同实现返回不同对象;

    Product: 建造者对象返回的最终产品(即由各个“部件”组合而成的完整对象);

    Director: 指挥者,充当客户的接口,调用建造者的接口进行“部件”的建造和“组装”,将完整对象返回给客户。符合迪米特原则。


    2.代码实现

     汽车类,chassis底盘轴距, engine发动机功率, body车身颜色

    /**
     * 汽车类,装配对象
     */
    class Car {
        private static int count = 0;
        private final int id;
    
        public Car() {
            id = count++;
        }
        /** 汽车底盘:轴距 */
        private int chassis;
        /** 发动机: 功率*/
        private double engine;
        /** 车身: 颜色 */
        private String body;
    
        /** 安装底盘*/
        public void setChassis(int wheelBase){
            chassis = wheelBase;
        }
        /** 安装发动机*/
        public void setEngine(double power){
            engine = power;
        }
        /** 安装车身 */
        public void setBody(String color) {
            body = color;
        }
    
        public int getChassis() { return this.chassis;}
        public double getEngine() { return this.engine;}
        public String getBody() {return this.body;}
    
        public String toString() {
            return "car" + id + "( chassis:" + chassis + ", engine:" + engine +
                    ", body:" + body +")" ;
        }
    }

    汽车建造者抽象类,结合模版方法模式, build方法实际就是模版方法。而chassis,engine,body则由具体子类实现。

    /** 抽象建造者类 */
    abstract class CarBuilder {
        /** 建造对象的"模板" */
        protected Car carTemp;
        /** Builder对象*/
        public CarBuilder() {
            carTemp = new Car();
        }
    
        /**
         * 将建造者中的汽车模板中的属性取出来赋值给实际的Car对象
         * 实际上,setChassis等是不需要的,因为这里没有参数
         * 如果有参数,就需要这些方法
         */
        public Car build() {
            Car car = new Car();
            car.setChassis(carTemp.getChassis());
            car.setEngine(carTemp.getEngine());
            car.setBody(carTemp.getBody());
            return car;
        }
    
        /** 装配底盘 */
        public abstract CarBuilder buildChassis();
        /** 装配发动机 */
        public abstract CarBuilder buildEngine();
        /** 装配车身 */
        public abstract CarBuilder buildBody();
    }

    低配车建造者

    /**
     * 建造低配汽车的建造者
     */
    class LowCarBuilder extends CarBuilder {
    
        @Override
        public CarBuilder buildChassis() {
            carTemp.setChassis(4200);
            return this;    //这样写可以实现对象方法的"链式调用"
        }
    
        @Override
        public CarBuilder buildEngine() {
            carTemp.setEngine(150.0);
            return this;
        }
    
        @Override
        public CarBuilder buildBody() {
            carTemp.setBody("Block");
            return this;
        }
    
    }

    高配车建造者

    /**
     * 建造高配汽车的建造者
     */
    class HighCarBuilder extends CarBuilder {
    
        @Override
        public CarBuilder buildChassis() {
            carTemp.setChassis(4250);
            return this;    //这样写可以实现对象方法的"链式调用"
        }
    
        @Override
        public CarBuilder buildEngine() {
            carTemp.setEngine(180.0);
            return this;
        }
    
        @Override
        public CarBuilder buildBody() {
            carTemp.setBody("White");
            return this;
        }
    
    }

    指挥者,作为客户调用建造者的实际接口,持有CarBuilder的引用,这里使用简单工厂模式,根据客户所传type来决定实例化哪一个Builder

    /**
     * 指挥者
     */
    class Director {
        private CarBuilder builder;
        private static HashMap<String, Class<? extends CarBuilder>> map = new HashMap<>();
        static {
            // 实际项目中可以扫描某包下的相关对象注册class
            map.put("low", LowCarBuilder.class);
            map.put("high", HighCarBuilder.class);
        }
    
        private Car invoke() {
            return builder.buildBody()
                        .buildChassis()
                        .buildEngine()
                        .build();
        }
    
        public Car build(String type) throws IllegalAccessException, InstantiationException {
            builder = map.get(type).newInstance();
            return invoke();
        }
    }

    客户调用

    public class BuilderDemo {
        public static void main(String[] args) throws InstantiationException, IllegalAccessException {
            Director director = new Director();
            Car car = director.build("low");
            System.out.println(car);
            car = director.build("high");
            System.out.println(car);
        }
    }

    输出结果,这里car1,car3,数字用来统计创建了多少Car实例,因为在建造者中创建了两个carTemp为car0和car2

    car1( chassis:4200, engine:150.0, body:Block)
    car3( chassis:4250, engine:180.0, body:White)

    3.总结

    建造者模式的意图是把构建复杂对象的逻辑分离出来,分别创建复杂对象的某个部分,然后将它们装配起来形成一个完整的复杂对象。实际开发过程中,笔者公司大量使用了lombok中的@Builder注解配合@Data,使创建DTO时自动实现了建造者模式,好处是你可以决定创建出什么样子的DTO对象而无需实现创建逻辑。还有一个地方会经常用到创建者模式,就是在做一些格式转换之类的工作时,往往创建一个Converter父类,然后根据实际业务需求,不同的子Converter类进行不同的格式转换,通常还需要一个Director或者叫Manager之类名称的类,来调用Converter,如果要实现动态地转换,在Director中还可能用到注册方式的简单工厂方法创建具体的Converter,结合创建者模式对格式进行转换。

    总之,不同的设计模式要多学多用,很多时候它们不是孤立存在而是相生相伴的,这也许就是设计模式最困难,最需要大量实践经验之处吧。

  • 相关阅读:
    opengles 2.0 渲染Yuv视频
    wzplayer for android 版本面世
    wzplayer for android V1.0快出炉了
    wzplayer for android V1.0快出炉了
    wzplayer for android V1.0
    opengles 2.0 渲染Yuv视频
    新浪微博发布案例
    前端开发单位em
    vue 做一个简单的TodoList
    jquery对类的操作,添加,删除,点击添加,再点击删除
  • 原文地址:https://www.cnblogs.com/yxlaisj/p/10497698.html
Copyright © 2011-2022 走看看