美文网首页
创建同一类型不同的对象——Builder来秀一波

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

作者: AmosZhu | 来源:发表于2019-08-03 14:43 被阅读0次

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

    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<Categorie> categories;
        
            /**
             * 定义一个构建器 并且约束该构建器的类型
             * 允许方法链在子类中运行正常,不需要进行类型强转
             *
             * @param <T>
             */
            abstract static class Builder<T extends Coffee.Builder<T>> {
                /**
                 * 将Pizza所有的类型属性枚举清空
                 */
                EnumSet<Coffee.Categorie> 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<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来秀一波

          本文链接:https://www.haomeiwen.com/subject/ruujdctx.html