zoukankan      html  css  js  c++  java
  • Java 帝国之建造者模式

    Java 帝国之建造者模式

    本文来自王钦誉的投稿,老刘做了较大修改。

    原文地址:

    https://xiaoqinyu0000.github.io/2018/06/11/Java/JavaBuilder/

    据说在帝国建立初期,人类眼里的Java程序就是一个黑乎乎的命令行窗口,里面显展示着大片大片的代码和文字。

    那是一个灰暗的年代, 几乎没有人愿意天天与这些黑窗口打交道。

    国王决心让帝国变得多姿多彩,于是下令建立图形村,研究Java的图形和图像。

    经过艰苦卓绝的努力,我们掌握了GUI编程的核心技术,帝国终于可以在人类设备上展示图形界面了。 

    99个重载函数

    我是图形村的一个代表,专门对外提供一种图形: Square(正方形)

    给你看看我最近接的几个单子:

    用户一的需求是一个50 * 50的蓝色正方形;

    用户二的需求是一个50 * 50的蓝色正方形,还要一个黑色的边框;

    …....

    Square对象可以很简单(例如用户一要的就是一个蓝色的正方形),也可以非常复杂(形状、颜色、大小、边框、阴影、层次等等)。

    用户需要什么样的图形,只需要把对应的属性(长、宽、颜色...)告诉我就行...

     1class Square {
    2    public Square(int size, int color) { ... }
    3
    4    public Square(int size, int color, int borderColor) { ... }
    5
    6    public Square(int size, int color, int borderColor, float radius) { ... }
    7
    8    public Square(int size, int color, int borderColor, float radius, float strokeDashGap) { ... }
    9
    10    public Square(int size, int color, int borderColor, float radius, float strokeDashGap, float shadow) { ... }
    11    ......
    12}

    (友情提示:可左右滑动)

    为此,一个Square的构造函数, 我整整重载了99次!

    不过带来的好处就是不管用户想要一个什么样的图形,只需调用一个方法,一行代码就可以搞定啦。

    比如,用户1想要一个50*50的蓝色正方形,你需要给我3个参数便可以获得Square对象:

    • 大小:50 * 50

    • 颜色:Color.Blue

    Square square = new Square(50, Color.Blue);

    想再加一个黑色边框,只需要增加一个参数就行:

    • 边框:Color.Black

    Square square = new Square(50, Color.Blue, Color.Black);

    仅需一行代码就可以拿到Square对象,然后就可以在人类的设备上显示图形啦。

    没有什么是一个重载方法解决不了的,如果有,那就两个重载方法…

    每当有人抱怨说,从你这99个方法中找到一个合适的方法实在是太难了!  我就扔给他一个文档,里边详细描述着每个方法的参数,含义,只要读一遍,绝对没问题!

    我的坚持引发了大家的怨气,当抱怨的人越来越多,我有点慌了,赶紧去找村长,希望他能给出点注意。

    新设计

    村长看了一会儿我的API,叹了口气说:“怪不得人家抱怨,你看看你的设计!”

    Square square = new Square(50, Color.Red, Color.Blue, 5, Color.Black, 4, 9, Color.Red, 4, 8);

    我一瞧,有什么奇怪的嘛?不就是创建一个Square对象吗?

    他指着这行代码:“你看这行代码,能够清晰得知道要的是怎么样的图形吗?假设要你把阴影颜色改了,你知道改哪个值吗?”

    我说:“稍等,我查下文档。”

    3分钟后我说:“好像是改第5个参数吧。”

    “每次用你的服务都要去查文档,从N个重载方法里找到对应所需的方法,再看看每个属性的含义,你觉得好用吗?” 村长厉声斥责。

    我心想,这确实是问题,然而世间之事哪能十全十美呢?

    我叹了口气:“Square 是复杂对象, 有很多属性和组合方式…有形状、颜色、长宽、边框、阴影、内外边距….”。

    “你啊,真是不思进取,你想想,还有什么好的设计? ”

    我说:“也许可以做出这样? ”

    1class Square {
    2    public Square() {...}
    3    public void setSize(int size) {...}
    4    public void setColor(int color) {...}
    5    public void setBorderSize(int size) {...} 
    6    public void setBorderColor(int color) {...}
    7    public void setPadding(int left, int top, int right, int bottom) {...}
    8    ...
    9}

    (友情提示:可左右滑动)


    其实就是把各个属性拆分成各个方法,让用户去组合他们需要的属性!

    1Square square = new Square();
    2square.setColor(Color.Blue);
    3square.setBorderSize(5);
    4square.setBorderColor(Color.Black);
    如何对参数进行检查?

    村长说:“这其实是一种类似JavaBean的方法, 比之前好了一点,但是你想过这个问题没有? 假设一些属性依赖另外一些属性,如果设置了属性A,但是没有设置属性B,那Square对象其实是有问题的,对于这种情况,你怎么处理?你在什么地方检查这种互相依赖的逻辑? ”

    村长一下子就击中我的要害了,果然有两下子。

    我赶紧请教:“您有何高见?”

    村长说道:“你可以开放一个静态内部类专门用来与外界打交道,来收集用户想要设置的参数并且做检查。”

     1class Square {
    2
    3    private int color;
    4    private int borderSize;
    5    ......
    6    //private,让外面无法直接创建
    7    private Square(Builder builder) {    
    8        this.color = builder.color;
    9        this.borderSize = builder.borderSize;
    10        ......
    11    }
    12
    13    public static class Builder {
    14        private int color;
    15        private int borderSize;
    16        ......
    17
    18        public void setColor(int color) { }
    19        public void setBorderSize(int size) { }    //边框大小
    20        public void setBorderColor(int color) { }    //边框颜色
    21        public void setPadding(int left, int top, int right, int bottom) { }
    22        ...
    23
    24        public Square build() {
    25            ...检查参数之间的关系是否设置正确...
    26            return new Square(this);
    27        }
    28    }
    29}

    看到这个代码,我已经不由自主地在纸上模拟用户的使用场景了:

    1Square.Builder builder = new Square.Builder();
    2builder.setSize(50);
    3builder.setColor(Color.Blue);
    4builder.setBorderSize(5);
    5builder.setBorderColor(Color.Black); 
    6//在build方法中会检查参数是否正确。
    7Square square = builder.build();

    可是这个调用方式可是比我之前提供的API复杂得多呀!如果旧的API,只用一行代码就可以可以了!

    链式调用

    村长似乎看到了我的疑惑,笑了笑问到:”你知道链式调用吗?“。

    紧接着,他又展示了一段代码,里面静态内部类Builder的所有设置属性的方法的返回值居然变了:

     1class Square {
    2    ...
    3    public static final class Builder {
    4        public Builder setColor(int color) {
    5            ......
    6            return this;
    7        }
    8        public Builder setBorderSize(int size) {
    9            ......
    10            return this;
    11        }
    12        public Builder setBorderColor(int color) {
    13            ......
    14            return this;
    15        }
    16        public Builder setPadding(int left, int top, int right, int bottom) {
    17            ......
    18            return this;
    19        }
    20        ...
    21        public Square build() {
    22            ...检查参数之间的关系是否设置正确...
    23            return new Square(this);
    24        }
    25    }
    26}

    每一个Builder都返回了一个this,可是这又有什么用呢。

    结合之前对方说的链式调用,我内心突然想到某种可能!

    赶紧对之前调用方式的Demo做了一点修改:

    1Square square = new Square.Builder()
    2    .setSize(50)
    3    .setColor(Color.Blue)
    4    .setBorderSize(5)
    5    .setBorderColor(Color.Black)
    6    .build();

    居然用一行代码就完成了调用,而且每个方法和属性都是如此得清晰、如此的明了,让人陶醉其中,不能自拔!

    “看来,好的API设计远远重要于一份面面俱到的文档。” 我不由地感慨。

    建造者模式

    村长说道:“是啊,其实这种方式有个名称,叫做建造者模式! 对于你这个场景很适用。”

    “Builder模式? 23种设计模式之一, 我学习过,好像不是这样的啊?”  说着我展示了一个UML 类图。

    “真是死脑筋,我们这个是个简化版嘛!你看看这个图,Client 可以认为就是Director, 对于Builder,我们没有没有接口,直接就是具体类。 核心思想还是一样的嘛!”

    (完)

  • 相关阅读:
    filter&map&reduce
    Linux通过进程ID查看文件路径
    PyCharm使用最多也最常用默认快捷键介绍
    Python中的深浅拷贝
    类加载器&反射
    Java web.xml 配置详解
    SpringMVC + Spring + MyBatis 整合 + Spring shrio + easyUI + 权限管理框架,带shrio session和shrio cache集群实现方案
    JAVA大数据数组排序
    高访问量WEB开发中的架构模式,学习从点滴开始
    WEB项目会话集群的三种办法
  • 原文地址:https://www.cnblogs.com/bigben0123/p/9375833.html
Copyright © 2011-2022 走看看