zoukankan      html  css  js  c++  java
  • 一段对话讲完建造者模式

    截止今天,小秋学习Java刚刚满三个月。此刻的小秋感觉自己Java学的还不错,想去帅地面前炫耀一番,于是,就发生了一下一番对话…..

    得意的小秋

    帅地:嗨,小秋,看你今天气色不错啊。最近Java学的怎么样了?

    小秋:说实话,比起三个月前我学指针的那段日子,这Java太友好了,感觉就是分分种的事(暗自得意)。

    帅地:我靠,你这口气还挺大啊。从c的面向过程到Java的面向对象,还习惯不?

    小秋:哎,还行,无非就是“一切皆对象”,Java中的对象感觉类似于C中的结构体。反正不过三七二十一,我脑子里都把他们当成是一个对象就得了。(内心自我感觉良好)

    帅地:看你也学了三个月了,要不我随便拿道题考考你?(让你不谦虚,暗自偷笑)

    小秋:好啊,正好练练手(嘿嘿,终于可以展现实力了)。

    重载多次的构造函数

    帅地:假如有一个蛋糕Cake对象,蛋糕这个对象有一个必选属性size,还有一些可选属性apple,banana,orange,mango等,必选属性代表用户必须要指定蛋糕的大小,可选属性代表这些蛋糕要加哪些材料。

    小秋:这个很简单啊,创建一个Cake类,里面有size,apple,banana,orange,mango属性,然后构造器的参数里指定size这个参数就可以了。我直接上代码吧:

    public class Cake {
        private int size;
        private String apple;
        private String banana;
        private String orange;
        private String mango;
        //new时必须给出size
        public Cake(int size) {
            this.size = size;
        }
    }
    

    帅地:可选参数呢?我要new一个size=30,并且添加apple的蛋糕怎么办?

    小秋:哦,我写的太快,忘了重载了,稍等(心想,这还不简单)。

    public class Cake {
        private int size;
        private String apple;
        private String banana;
        private String orange;
        private String mango;
    
        public Cake(int size) {
            this.size = size;
        }
        public Cake(int size, String apple) {
            this.size = size;
            this.apple = apple;
        }
        public Cake(int size, String apple, String orange) {
            this.size = size;
            this.apple = apple;
            this.orange = orange;
        }
        public Cake(int size, String apple, String orange, String mango) {
            this.size = size;
            this.apple = apple;
            this.orange = orange;
            this.mango = mango;
        }
    }
    

    小秋:这下总可以了吧,你要加哪些料,你就使用哪个构造器。全部给你重载了。

    帅地:(露出狡猾的表情)写构造函数倒是挺快的,那如果我要只加apple和mango的蛋糕呢?

    小秋:啊?好吧,你这是逼我把所有组合的构造器都写出来。

    于是,小秋把所有构造器的组合都写了出来。由于size是个必须参数,把其他四个可选参数进行组合,一共有16种。

    噼里啪啦,劈里啪啦,小秋一口气把他们全部写出来了

    public class Cake {
        private int size;
        private String apple;
        private String banana;
        private String orange;
        private String mango;
    
        public Cake(int size){
            this.size = size;
        }
        public Cake(int size, String apple){
            this.size = size;
            this.apple = apple;
        }
        public Cake(int size, String banana){
            this.size = size;
            this.banana = banana;
        }
        .....
    }
    

    小秋:好了,这下,你要啥组合有啥组合了。

    帅地:四个可选参数你就写了这么一大堆参数了。确定这样写?

    小秋:我觉得挺好的啊,反正很快,多写几个就多写几个吧。

    帅地:那如果给你6个可选参数呢?

    这时候小秋偷偷算了一些,发现一共有74种组合?

    小秋:不就是74种组合,我觉得问题不是很大(心有点虚)。

    帅地:那万一有10个可选参数呢?

    小秋:…..

    帅地:而且你重载那么多构造器,用户在在new的时候,第一个参数和第二个参数代表什么,用户混乱了怎么吧?例如,你有个apple+banana的构造器

    public Cake(int size, String apple, String banana){
        this.size = size;
        this.apple = apple;
        this.banana = banana;
    }
    

    但是用户在new的时候,可能忘记了参数的顺序

    Cake cake = new Cake(size,“banana”,“apple”)。
    

    小秋:我会提供相应的文档啊,忘记了可以看文档勒。

    帅地:几百个构造函数,而且还那么相似,你去看下文档试试,然后说说你的心情。

    小秋:……(不知所措)。

    通过Javabean的模式

    帅地:有没其他什么办法?

    小秋:我想到另一种办法了,我可以通过set和get方法来设置可选参数的。我直接上代码你看看

    public class Cake {
        private int size;
        private String apple;
        private String banana;
        private String orange;
        private String mango;
    
        public Cake(int size) {
            this.size = size;
        }
        //通过set来添加材料
        public void setApple(String apple) {
            this.apple = apple;
        }
    
        public void setBanana(String banana) {
            this.banana = banana;
        }
    
        public void setMango(String mango) {
            this.mango = mango;
        }
    
        public void setOrange(String orange) {
            this.orange = orange;
        }
    }
    

    此时的小秋有点得意….

    帅地:挺不错,这种方法比刚才的好多了,又简洁。

    此时如果要new一个apple+orange+mango的蛋糕的话,代码如下:

    Cake cake = new Cake(30);
    cake.setApple("apple");
    cake.setOrange("orange");
    cake.setMange("mange");
    
    参数依赖检查问题

    帅地:这种方法也是有缺点,例如用构造器重载时一行代码就可以搞定了,现在要用四行代码。

    小秋:反正我觉得这样很nice(得意中…)。

    帅地:不过这样写有一个致命的缺点,假如那些属性之间存在依赖性的话,怎么办?例如Cake多了A,B两个属性,并且这两个属性之间存在依赖关系。如果你设置了属性A,但是没有设置属性B,那么这个Cake对象就会出问题。或者属性的先后顺序设置也可能会导致出现问题。对于这种情况,你在什么地方检查这种相互依赖的逻辑?

    小秋:有点蒙蔽,不知所措….。

    小秋:那你说怎么办?

    静态内部类

    帅地:其实你已经做的相当不错了,不过我今天就教你另外一个办法,我们可以开放一个静态内部类专门用来与外界打交道,用来收集用户想要设置的属性并且做检查。直接上代码:

    public class Cake {
        private int size;
        private String apple;
        private String banana;
        private String orange;
        private String mango;
        //private,让外面无法直接创建
        private Cake(Builer builer) {
            this.size = builer.size;
            this.apple = builer.apple;
            .....
        }
        //专门用来与外界打交道
        public static class Builer {
            private int size;
            private String apple;
            private String banana;
            private String orange;
            private String mango;
            public void setSize(int size) {
                this.size = size;
            }
            //为了省点代码,其他的省略
            public Cake build() {
                //检查参数之间的依赖关系是否正确
                return new Cake(this);
            }
            .....
        }
    }
    

    假如我要new一个apple+orange的Cake

    Cake.Builer builer = new Cake.Builer();
    builer.setSize(30);
    builer.setApple("apple");
    builer.setOrange("orange");
    //创建一个蛋糕
    Cake cake = builer.build();
    

    帅地:这种方法牛吧?这还不够,我们还可以采用链式调用的方法。

    链式调用
    public class Cake {
        private int size;
        private String apple;
        private String banana;
        private String orange;
        private String mango;
        //private,让外面无法直接创建
        private Cake(Builer builer) {
            this.size = builer.size;
            this.apple = builer.apple;
            .....
        }
        //专门用来与外界打交道
        public static class Builer {
            private int size;
            private String apple;
            private String banana;
            private String orange;
            private String mango;
            //返回参数改为Builer
            public Builer setSize(int size) {
                this.size = size;
                return this;
            }
    
            public Builer setApple(String apple) {
                this.apple = apple;
                return this;
            }
            //为了省点代码,其他的省略
            public Cake build() {
                //检查参数之间的依赖关系是否正确
                return new Cake(this);
            }
        }
    }
    

    如何使用?

    Cake cake = new Cake.Builer()
                .setSize(30)
                .setApple("apple")
                .setOrange("orange")
                .build();
    

    一行代码就搞定了。

    帅地:厉害吧?

    小秋:涨知识了,看来我还是太年轻了,以后得好好向帅地学习。

    建造者模式

    帅地:其实,上面那种方法算是23种设计模式中的其中一种—-建造者模式。不过这只是一个简化版的建造者模式。

    对于建造者模式,具体的UML图是这样的:

    在这个UML图中,Builder是一个接口,定义一套规范,而我们使用的例子中,没有使用接口,直接使用具体类,但核心思想还是一样的。

    其核心思想就是:将一个复杂的对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

    小秋:哇,强啊。我要给你点赞…..

    算是第二次采取对话的方式写….,以后会多采取这种方式来写勒。

    关注公我的众号:苦逼的码农,获取更多原创文章,后台回复礼包送你一份时下热门的资源大礼包。同时也感谢把文章介绍给更多需要的人

  • 相关阅读:
    Hashmap
    string字符串分词
    关于链表的几道经典例题
    【C语言】链表(LinkedList)的建立与基本操作
    01迷宫
    2019 数学联赛 题解 / 游记
    一种简单方法构造 n 元有限域
    Python逆向某网站之 AES解密数据
    我发现了一个网站Bug,然后反馈了
    在多线程中使用静态方法是否有线程安全问题
  • 原文地址:https://www.cnblogs.com/kubidemanong/p/9650964.html
Copyright © 2011-2022 走看看