• 本文仅供《码农翻身》公众号发布
  • 感谢刘欣老师的指点、修改和排版

帝国图形始之初

图形村,一个在Java帝国里妇孺皆知的,最美丽的村庄。

整个Java帝国就是因为我们村而变得多姿多彩。

据说,在帝国建立初期,人类眼里的Java的程序就是一个黑窗口,里面显示一大片文字。

几乎没有人愿意与天天与这些黑窗口打交道。

那是一个灰暗的年代。

也从那时候起,我们村长就决定举全村之力,研究Java之下的图形与图像。

也许是诚意感动了上天。

在牺牲了好几代村长之后,我们村终于掌握了核心技术,让帝国能够在人类设备的舞台上,展现自己的妖娆,美丽。

Drawable对象,说人话就是各种各样能在用户设备上显示的图形。

我是村里的代表(DrawableUtils.java),给你看看我最近接的几个单子:

Demo

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

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

….

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

所以,我把Drawable对象称为“复杂对象”。

这年头,做服务业不容易呀~

为了让用户不需要记住太复杂的调用方法,我只开放了一个静态方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
用户需要什么样的图形,只需要把对应的属性(长、宽、颜色...)告诉我就行...
```Java
class DrawableUtils {
public static Drawable getDrawable(int width,int height,int color){}
public static Drawable getDrawable(int width,int height,int color,int borderColor){}
public static Drarwale getDrawable(int width,int height,int color,int borderColor,float radius){}
public static Drarwale getDrawable(int width,int height,int color,int borderColor,float radius,float strokeDashGap){}
public static Drarwale getDrawable(int width,int height,int color,int borderColor,float radius,float strokeDashGap,float shadow){}
......
}

为此,我整整重载了99次,我的天哟….我觉得帝国要是有一个劳模榜,前三名肯定有我。

虽然很累,但一切都是值的。

因为不管用户想要什么一个什么样的图形,只需调用一个方法,一行代码就可以搞定啦。

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

  • 宽度:50
  • 长度:50
  • 颜色:Color.Blue
1
Drawable drawable = DrawableUtils.getDrawable(50, 50, Color.Blue);

又比如图2,想再加一个黑色边框,只需要增加一个参数就行:

  • 边框:Color.Black
1
Drawable drawable = DrawableUtils.getDrawable(50, 50, Color.Blue, Color.Black);

一行代码拿到Drawable对象就可以在人类的设备上显示图形啦。

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

我真的佩服我的机智!

合久必分,链式调用

可能是名声大噪,被网友称为网红的缘故,每天有络绎不绝的人来拜访我。

这一天,有一个叫张大胖的找了上门。

我决定见一见他,享受一下村外人的赞美。

没见到对方见到我第一句话就是:“我是代表正义来消灭你那些垃圾的API的,好难用哇!”

我去…这感情来了个神经病?

当然,现在我是名人了,说话做事得看得起蹲在外面的狗仔队嘛!

我微微一笑,耐着性子问:“这位先生,此话和解?”

对方给我看了一行代码:

1
Drawable drawable = DrawableUtils.getDrarwable(50, 50, Color.Red, Color.Blue, 5, Color.Black, 4 , 9, Color.Red, 4, 8);

我一瞧,这不是就是通过我的API获得一个Drawable对象嘛~有什么奇怪的嘛?

他指着这行代码,”你看这行代码,能够清晰得知道要的是怎么样的图形吗?

假设要你把阴影颜色改了,你知道改哪个值吗?

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

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

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

”停!“对方毫不客气地打断了我的话。“在来之前,我就想到一个API的设计方案,特来跟你讨论。“

真是个没礼貌的家伙,我点了点头,示意他继续讲下去。

“针对这种复杂对象的构建。既然一个方法的方法行不通,那便用多个方法”:

对方不知道从哪抽出一张卡片,上面还印有我的大名:

1
2
3
4
5
6
7
8
9
10
class DrawableUtils {
public void setShare(Share share){} //设置形状,比如Share.Rectangle,Share.round
public void setSize(int width, int height){}
public void setColor(int color){}
public void setBorderSize(int size){} //边框大小
public void setBorderColor(int color){} //边框颜色
public void setPadding(int left, int top, int right, int bottom){}
...
public Drawable getDrawable(){}
}

原来不用重载,把各个属性拆分成各个方法,让用户去组合他们需要的属性。

在对象属性比较多的情况下,这样设计API确实比较好。

其实这不算什么…我也能想到。

对方也许看到了我的不屑:”在设计完这些接口之后,我发现你这个类会变得超级臃肿。因为你这个类不止要提供对外的API。里面肯定还要对这些属性进行校验、转换、处理、合并,再创造出一个Drawable对象。类承担的事情太多了,不符合设计原则中的单一职责原则。至少,我觉得与外界打交道的这些API可以抽出来。“

握草,击中我的要害了,这厮果然有两下子。

因为其实不管是新的设计方案还是旧的那一套API,都存在这个问题。

赶紧向对方请教一番。

对方笑了笑,又扔了一张卡片给我,”你可以开放一个静态内部类专门用来与外界打交道“:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class DrawableUtils {
private Builder mBuilder;
private DrawableUtils(Builder builder){ //private,让外面无法直接创建
this.mBuilder = builder;
}
public Drawable getDrawable(){}
public static final Builder {
public void setShare(Share share){} //设置形状,比如Share.Rectangle,Share.round
public void setSize(int width, int height){}
public void setColor(int color){}
public void setBorderSize(int size){} //边框大小
public void setBorderColor(int color){} //边框颜色
public void setPadding(int left, int top, int right, int bottom){}
...
public DrawableUtils builder(){
return new DrawableUtils(this);
}
}
}

搞一个静态内部类来提供API给外界访问,最后通过

1
2
3
4
5
6
7
8
9
10
11
12
这个设计确实从调用到实现把职责区分开了,也可以隐藏具体的实现类,对用户也好!
我已经不由自主得在纸上模拟用户的使用场景了:
```java
DrawableUtils.Builder builder = new DrawableUtils.Builder();
builder.setSize(50,50);
builder.setColor(Color.Blue);
builder.setBorderSize(5);
builder.setBorderColor(Color.Black);
Drawable drawable = builder.builder().getDrawable();

看到笔下的代码,心里一阵发堵啊~

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

或许对方看到我内心的纠结,笑了笑问:”你知道链式调用吗?“。

我内心真发堵呢?哪听到他说什么?

紧接着,他又扔了一张卡片过来,里面静态内部类Builder的所有设置属性的方法的返回值居然变了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class DrawableUtils {
...
public Drawable getDrawable(){}
public static final Builder {
public Builder setShare(Share share){ ...
return this;
}
public Builder setSize(int width, int height){ ...
return this;
}
public Builder setColor(int color){ ...
return this;
}
public Builder setBorderSize(int size){...
return this;
}
public Builder setBorderColor(int color){...
return this;
}
public Builder setPadding(int left, int top, int right, int bottom){...
return this;
}
...
public DrawableUtils builder(){
return new DrawableUtils(this);
}
}
}

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

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

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

1
2
3
4
5
6
7
Drawable drawable = new DrawableUtils.Builder()
.setSize(50,50)
.setColor(Color.Blue)
.setBorderSize(5)
.setBorderColor(Color.Black)
.builder()
.getDrawable();

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

看来,好的API设计远远重要于一份面面俱到的文档。

闭关重构悟设计

闭关七天七夜,我终于根据对方的设计方案把这套新的API给完成。

看着设计好的API,我发现内部实现比之前的旧API的实现复杂多了。

但各个地方看起来又非常清晰明白,方便调用,也方便维护。

心里想了想,这大概就是设计的力量吧!

果不其然,新的API推出之后,我的名气在帝国可谓一时无两。

当大家问我新的API的设计理念的时候,我脑里浮现了张大胖临走时留下的最后一张卡片:

”建…造…者….设…计…模…式!“

商人重利成Director

俗话说,有人的地方就有江湖!

有这么一群商(J)人(S),他们通过分析已有的API进行二次封装,通过向大家提供更为便捷的API调用来收费。

这次,就有一个叫DrawableDirector的商人通过对DrawableUtils的接口进行二次封装:

1
2
3
4
5
6
7
8
9
class DrawableDirector {
public static Drawable createRedColorRectangle(){ //创造一种红色的矩形
return new DrawableUtils.Builder().set.Color(Color.Red)......builder().getDrawable();
}
public static Drawable createBlueColorRectangle(){ //创造一种蓝色的矩形
return new DrawableUtils.Builder().setColor(Color.Blue).......builder().getDrawable();
}
}

我当然管不了这些事,反正最终还不是要到我这里来。

后来有好事者把这么一种基于建造者设计模式,把常用的构建过程封装起来的类称为Director

这种做法也正式被纳入建造者设计模式的设计思想中。

但大家在设计API的时候很少把Director考虑进去,可能是太懒了吧!

不堪996,约法三章

虽然我是劳模网红,但这日复一日也是很累诶!

别人都有双休日,法定节假日还放假,凭什么我就要996。

我觉得在村里找些小伙伴来磨砺(#滑稽)。

所谓无规矩不成方圆,以下约法三章。

  • 章法一:能吃苦耐劳,完成用户所要求的Drawable的创建。
  • 章法二:基于章法一的特点,大家必须实现IDrawableWorker接口,实现getDrawable()方法。
1
2
3
4
public interface IDrawableWorker {
Drawable getDrawable();
}

这不,我自己就实现了:

1
2
3
4
5
public class DrawableUtils implements IDrawableWorker {
@Override
public Drawable getDrawable(){...}
}
  • 章法三:轮到谁值班,Builder内部静态类就把需求给谁。
1
2
3
4
5
6
7
8
9
10
11
12
public class DrawableUtils implements IDrawableWorker {
@Override
public Drawable getDrawable(){...}
public static final Builder {
...
public IDrawableWorker builder(){ //返回IDrawableWorker对象
return new DrawableUtils(this);
}
}
}

最近想去旅游,让隔壁大傻来顶一段时间。

首先,依据章法二实现IDrawableWorker接口:

1
2
3
4
5
6
7
public class Dasha implements IDrawableWorker {
@Override
public Drawable getDrawable(){
return new Drawable("天灵灵、地灵灵,老天赐我一个Drawable") ;
}
}

让Builder把活交给大傻,出去玩啦,嘿嘿嘿:

1
2
3
4
5
6
7
8
9
10
11
12
public class DrawableUtils implements IDrawableWorker {
@Override
public Drawable getDrawable(){...}
public static final Builder {
...
public IDrawableWorker builder(){
return new Dasha(this);
}
}
}

用户还是跟以前一样地调用,然而却不知道工作的人已经换了一个。

猜一猜

施主,既然你耐心地看到这。那就送你一张UML图。

猜一猜里面的各个元素对应着本文的哪些类:

3

最后更新: 2018年07月26日 00:17

原始链接: https://xiaoqinyu0000.github.io/2018/06/11/Java/JavaBuilder/