- 本文仅供《码农翻身》公众号发布
- 感谢刘欣老师的指点、修改和排版
帝国图形始之初
图形村,一个在Java帝国里妇孺皆知的,最美丽的村庄。
整个Java帝国就是因为我们村而变得多姿多彩。
据说,在帝国建立初期,人类眼里的Java的程序就是一个黑窗口,里面显示一大片文字。
几乎没有人愿意与天天与这些黑窗口打交道。
那是一个灰暗的年代。
也从那时候起,我们村长就决定举全村之力,研究Java之下的图形与图像。
也许是诚意感动了上天。
在牺牲了好几代村长之后,我们村终于掌握了核心技术,让帝国能够在人类设备的舞台上,展现自己的妖娆,美丽。
Drawable对象,说人话就是各种各样能在用户设备上显示的图形。
我是村里的代表(DrawableUtils.java),给你看看我最近接的几个单子:
用户一的需求是一个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,想再加一个黑色边框,只需要增加一个参数就行:
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){} 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){ this.mBuilder = builder; } public Drawable getDrawable(){} public static final Builder { public void setShare(Share share){} 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(){ 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图。
猜一猜里面的各个元素对应着本文的哪些类: