创建同一类型不同的对象——Builder来秀一波

在我们实际的开发需求中,经常需要创建同一类型的不同的对象,而且这个对象的特性还是随机可变的,这样就需要我们使用一种设计模式来满足复杂多变的场景;

1. 业务场景

我们以经常出现的喝咖啡为例,每种咖啡都有大杯小杯和中杯之分,然后咖啡可以添加糖、牛奶或者酒(?,这个口味就比较独特了),我们可以搭配大杯咖啡+糖、大杯咖啡+牛奶、小杯咖啡+酒、大杯+糖+牛奶等等,随着添加的种类的增多,搭配的种类也是不断的呈指数级的变化的,那么该如何使用代码来创建这些搭配呢?

2. Builder

现在来介绍我们今天的主角Builder,这里的Builder不是构建者模式,只是为了通过代码的设计,来实现一个方法链来实现对象的构建,下面通过一段代码来简单的介绍下Builder的用法

我们先看看传统的方式

    public class Coffee {
        /**
         * 咖啡的大小
         */
        private int size;
        /**
         * 是否添加糖
         */
        private boolean sugar;
        /**
         * 是否添加牛奶
         */
        private boolean milk;
        /**
         * 是否添加酒
         */
        private boolean wine;
        
        //.... 省略 setter/getter 方法
    }

那么传统的方式:

    Coffee coffee = new Coffee();
    coffee.setSize(1);
    coffee.setSugar(Boolean.TRUE);
    coffee.setMilk(Boolean.TRUE);
    coffee.setWine(Boolean.TRUE);

这样不是不可以 但是一旦我增加了添加的种类,改动的代码就比较多了,而且,这样的代码也显得比较low,没有丝毫美感可言,所以我们这里新增下静态内部类来优化下:

    public class Coffee {
        /**
         * 咖啡的大小
         */
        private final int size;
        /**
         * 是否添加糖
         */
        private final boolean sugar;
        /**
         * 是否添加牛奶
         */
        private final boolean milk;
        /**
         * 是否添加酒
         */
        private final boolean wine;
    
        public static class Builder {
            private int size;
            private boolean sugar;
            private boolean milk;
            private boolean wine;
    
            public Builder(int size) {
                this.size = size;
            }
    
            public Builder sugar(boolean sugar) {
                this.sugar = sugar;
                return this;
            }
    
            public Builder milk(boolean milk) {
                this.milk = milk;
                return this;
            }
    
            public Builder wine(boolean wine) {
                this.wine = wine;
                return this;
            }
    
            public Coffee builer() {
                return new Coffee(this);
            }
        }
    
        private Coffee(Builder builder) {
            this.size = builder.size;
            this.sugar = builder.sugar;
            this.milk = builder.milk;
            this.wine = builder.wine;
        }
    
        public int getSize() {
            return size;
        }
    
    
        public boolean isSugar() {
            return sugar;
        }
    
    
        public boolean isMilk() {
            return milk;
        }
    
    
        public boolean isWine() {
            return wine;
        }
    
    }

我们这里新增了静态内部类,其内部属性我们这里由于需求需要和父类的中的属性是保持一致的,方便我们在内部类中来操作属性对象的值,然后我们在内部类中提供方法来返回父类的对象,再接着,我们为了不让父类来自己实例化对象,我们将父类的构造器私有化,避免父类通过构建在来创建对象;

这样我们创建对象可以如下进行:

    Coffee coffee = new Coffee.Builder(1).milk(Boolean.TRUE).sugar(Boolean.TRUE).builer();

这样我们采用了链式的方式来创建对象,现在一看是不是很简洁了,看着也很舒服了

设计来满足上述需求

Builder方法虽然方便了很多,但是并不能解决我们之前的需求,同时代码量也是增大了很多,现在我们来使用一种设计方法来满足该需求;

该怎么去思考这个问题呢?

  • 我们先将咖啡所有可以添加的种类做成一个基类,并且种类作为一个枚举对象
  • 在这个基类中我们可以进行种类的添加
  • 然后子类中我们只要选择咖啡的大小就可以了
  • 子类决定创建对象的类型

按照上面的思路我们来进行代码开发:

    public abstract class Coffee {
        /**
         * Coffee定义可以添加的种类
         * 这里我们为了程序的演示方便,我们这里定义成枚举
         * 在实际的工作中,我们可以定义成一个种类的基类,利用多态的思想来实现
         */
        public enum Categorie {
            SUGAR,
            MILK,
            WINE
        }
    
        /**
         * 定义个Set集合来存储添加种类
         */
        final Set categories;
    
        /**
         * 定义一个构建器 并且约束该构建器的类型
         * 允许方法链在子类中运行正常,不需要进行类型强转
         *
         * @param 
         */
        abstract static class Builder> {
            /**
             * 将Pizza所有的类型属性枚举清空
             */
            EnumSet categories = EnumSet.noneOf(Coffee.Categorie.class);
    
            /**
             * 新增类型枚举 并且由子类返回对应的Pizza类型
             *
             * @param categorie
             * @return
             */
            public T addCategories(Coffee.Categorie categorie) {
                categories.add(Objects.requireNonNull(categorie));
                return self();
            }
    
            /**
             * 构建器,由子类去实现构建方法
             *
             * @return
             */
            abstract Coffee build();
    
            /**
             * 由子类来实现 返回子类的对象
             *
             * @return
             */
            protected abstract T self();
        }
    
        /**
         * Coffee 基类构造器
         *
         * @param builder
         */
        Coffee(Coffee.Builder builder) {
            categories = builder.categories.clone();
        }
    
    }

上面我们定义了Coffee基类,实现了我们之前思考的第一点,在基类中将需要添加的种类通过Builder定义了添加子类的方法,那么子类该如何定义呢?

    public class SizeCoffee extends Coffee {
        /**
         * 定义咖啡的大小
         */
        public enum Size {
            SMALL,
            LARGE,
            MEDIUM
        }
    
        private final SizeCoffee.Size size;
    
        /**
         * 定义静态内部类来实现对象的创建
         */
        public static class Builder extends Coffee.Builder {
            private final SizeCoffee.Size size;
    
            public Builder(SizeCoffee.Size size) {
                this.size = size;
            }
    
            /**
             * 返回子类对象
             *
             * @return
             */
            @Override
            Coffee build() {
                return new SizeCoffee(this);
            }
    
            @Override
            protected SizeCoffee.Builder self() {
                return this;
            }
        }
    
        /**
         * LargeCoffee构造器
         * 构造器私有化,为了防止通过父类来创建对象对象
         *
         * @param builder
         */
        private SizeCoffee(Builder builder) {
            super(builder);
            this.size = builder.size;
        }
    }

这样子类也创建好了,我们可以通过代码来创建

    Coffee coffee = new SizeCoffee.Builder(SizeCoffee.Size.LARGE)
                .addCategories(Coffee.Categorie.MILK)
                .addCategories(Coffee.Categorie.SUGAR).build();

这样的话,以后计算有什么添加种类或者咖啡的包装方式变换了,我们都可以基于该设计来实现我们的需求

你可能感兴趣的:(创建同一类型不同的对象——Builder来秀一波)