景
设计模式六大原则之四: 接口隔离原则.
简介
姓名 : 接口隔离原则
英文名 :Interface Segregation Principle
价值观 : 宁缺毋滥
个人介绍 :
- Clients should not be forced to depend upon interfaces that they don't use.(客户端不应该依赖它不需要的接口.)
- The dependency of one class to another one should depend on the smallest possible interface.(类间的依赖关系应该建立在最小的接口上.)
也用一个故事来讲这 2 句干巴巴的定义.
一小伙子跑到大城市的工厂打工, 工作了一年半载, 越来越觉得没劲, 每天干那么多活, 又领那么一点工资, 和他老爸抱怨这段时间的困扰, 老爸想着, 家里有个小作坊, 自己也一年不如一年了, 要不就让儿子回老家管理这小作坊. 小伙子熬不过这个年, 就跑回老家跟着老爸打理小作坊.
布娃娃
小作坊主要是做布娃娃的, 如上图, 工作在于打扮包装布娃娃, 工序有给布娃娃扎辫子, 穿衣服, 包装入箱, 打标签. 整个完整的流程都是一个人做的. 有很多个工人每天都在做这个事情.
老爸向小伙子诉苦, 感觉招工挺多人的, 生产力还是提不上去. 小伙子记着老爸的话, 在工厂里面观察了几天, 他发现每个工人都要做这 4 个打扮包装布娃娃的工序, 有些工人扎辫子很快但穿衣服很慢, 有些工人扎辫子很慢但穿衣服快, 他用了笔记本记下来: 李大姨扎辫子快, 王大妈穿衣服快, 就这样把每个人有效率的工作都记录下来.
一天晚上吃饭, 小伙子跟老爸说了自己观察到的现象, 也把本子拿给老爸看, 跟老爸商量: 可不可以做个尝试, 不要每个人负责打扮包装布娃娃全步骤, 而是按工序分开, 每个人只负责一个工序, 每个工人只干一件事, 更容易熟能生巧. 老爸听着觉得有道理.
第二天早上, 就到小作坊里, 召集了所有工人, 按小伙子的笔记上面的名单分工, 大家都做好各自负责的内容, 像流水线一样, 做好了就放到下个工序的地方, 让下个工序的人去做. 到了下班, 小伙子清点了今天工作的成果, 包装完成的娃娃比前一天多了 50% . 晚上小伙子跟老爸喝着百威吃起大肉庆祝一番.
这个故事你看了可能想骂爹骂娘, 跟上面的定义有啥毛关系? 故事只是把大家带入这个场景, 我们在工作中, 着手开发之前不都得先理清好需求背景, 这就是要讲接口隔离原则的背景, 通过代码来给大家讲解一下如何用好接口隔离原则.
父亲的运营模式
先看代码
- interface Work {
- void hairBraiding();
- void getDressed();
- void packingIntoTheBox();
- void makeTag();
- }
- class WangMather implements Work{
- @Override
- public void hairBraiding() {
- System.out.println("王大妈给布娃娃扎辫子");
- }
- @Override
- public void getDressed() {
- System.out.println("王大妈给布娃娃穿衣服");
- }
- @Override
- public void packingIntoTheBox() {
- System.out.println("王大妈把布娃娃装入箱子");
- }
- @Override
- public void makeTag() {
- System.out.println("王大妈给箱子打标签");
- }
- }
- class LiAunt implements Work {
- @Override
- public void hairBraiding() {
- System.out.println("李大姨给布娃娃扎辫子");
- }
- @Override
- public void getDressed() {
- System.out.println("李大姨给布娃娃穿衣服");
- }
- @Override
- public void packingIntoTheBox() {
- System.out.println("李大姨把布娃娃装入箱子");
- }
- @Override
- public void makeTag() {
- System.out.println("李大姨给箱子打标签");
- }
- }
- // 测试代码
- WangMather wangMather = new WangMather();
- wangMather.hairBraiding();
- wangMather.getDressed();
- wangMather.packingIntoTheBox();
- wangMather.makeTag();
- LiAunt liAunt = new LiAunt();
- liAunt.hairBraiding();
- liAunt.getDressed();
- liAunt.packingIntoTheBox();
- liAunt.makeTag();
在父亲管理下的小作坊, 是大家各自完成好一个布娃娃, 工作互不交接, 在这种运营模式下, 我们把所有工作都合并在一个接口 Work 是没有问题的. 有人可能要问, 不是说接口隔离么? 这里面 Work 接口的 4 个方法都可以分离开, 它们都是各自的工作内容. 稍等一下, 我们现在是基于老父亲运营的模式下实现, 如果小作坊一直都是这种模式运营, 这段代码有问题么? 其实没问题的, 我们根据当时的业务考虑, 在这种情况下, 把 Work 抽成 4 个接口不是不可以, 只是不现实, 每个工人都去实现一模一样的 4 个接口在老父亲运营模式下是不切实际.
儿子的运营模式
接下来介绍儿子的运营模式. 儿子提倡的是每个工人职责分明, 只负责一个事情, 在这种情况下, 如果还是用老父亲的 Work 接口会有什么问题呢? 上面我们说了, 李大姨扎辫子快, 王大妈穿衣服快, 所以李大姨被分配去给布娃娃扎辫子, 王大妈被分配去给布娃娃穿衣服. 我们沿用老父亲的 Work 接口实现, 代码如下
- class WangMather2 implements Work{
- @Override
- public void hairBraiding() {
- }
- @Override
- public void getDressed() {
- System.out.println("王大妈给布娃娃穿衣服");
- }
- @Override
- public void packingIntoTheBox() {
- }
- @Override
- public void makeTag() {
- }
- }
- class LiAunt2 implements Work {
- @Override
- public void hairBraiding() {
- System.out.println("李大姨给布娃娃扎辫子");
- }
- @Override
- public void getDressed() {
- }
- @Override
- public void packingIntoTheBox() {
- }
- @Override
- public void makeTag() {
- }
- }
看出问题来了么? 李大姨仅仅参与扎辫子工作, 王大妈参与了穿衣服工作, 但是却都要依旧实现其他 3 个多余的接口. 所以在儿子的运营模式下, 老父亲的 Work 接口需要重新分配, 以工序的角度分配, 而不是以完成一个布娃娃的角度分配. 总共有 4 个工序: 扎辫子, 穿衣服, 包装入箱, 打标签, 我们需要定义 4 个接口, 让员工去实现各自负责的工序接口. 代码如下
- interface Hair {
- void hairBraiding();
- }
- interface Dress {
- void getDressed();
- }
- interface Box {
- void packingIntoTheBox();
- }
- interface Tag {
- void makeTag();
- }
- /**
- * 李大姨给布娃娃扎辫子快
- */
- class LiAunt3 implements Hair {
- @Override
- public void hairBraiding() {
- System.out.println("李大姨给布娃娃扎辫子");
- }
- }
- /**
- * 王大妈给布娃娃穿衣服快
- */
- class WangMather3 implements Dress{
- @Override
- public void getDressed() {
- System.out.println("王大妈给布娃娃穿衣服");
- }
- }
- /**
- * 陈大叔包装快
- */
- class ChenUncle implements Box {
- @Override
- public void packingIntoTheBox() {
- System.out.println("陈大叔给布娃娃装箱");
- }
- }
- /**
- * 黄大姐贴标签快
- */
- class HuangSister implements Tag {
- @Override
- public void makeTag() {
- System.out.println("黄大姐给箱子打标签");
- }
- }
- // 测试代码
- LiAunt3 liAunt3 = new LiAunt3();
- WangMather3 wangMather3 = new WangMather3();
- ChenUncle chenUncle = new ChenUncle();
- HuangSister huangSister = new HuangSister();
- liAunt3.hairBraiding();
- wangMather3.getDressed();
- chenUncle.packingIntoTheBox();
- huangSister.makeTag();
这段代码看起来就很清晰了, 在儿子的运营模式下, 大家都是只做一道工序, 这样子实现就非常合理. 看了这个过程, 你理解了接口隔离原则了么? 再看一看上面的定义: 客户端不应该依赖它不需要的接口. 闭上眼睛, 静默 3 秒, 感受一下.
我们也可以回忆一下在工作中编写的代码, 是不是有遵守接口隔离原则? 在特定的场景下, 如果很多类实现了同一个接口, 并且都只实现了接口的极少部分方法, 这时候很有可能就是接口隔离性不好, 就要去分析能不能把方法拆分到不同的接口.
总结
接口隔离原则最最最重要一点就是要根据实际情况, 具体业务具体分析, 不能犯了上面说到的错误: 在老父亲的运营模式下, 按儿子的工序划分接口去实现, 那样子会得不偿失.
参考资料:《大话设计模式》,《Java 设计模式》,《设计模式之禅》,《研磨设计模式》,《Head First 设计模式》
希望文章对您有所帮助, 设计模式系列会持续更新, 感兴趣的同学可以关注公众号, 第一时间获取文章推送阅读, 也可以一起交流, 交个朋友.
公众号之设计模式系列文章
公众号
来源: http://www.jianshu.com/p/bcefaf40f65c