企业🤖AI智能体构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
### Extract Interface(提炼接口) 若干客户使用class 接口中的同一子集;或者,两个classes 的接口有部分相同。 将相同的子集提炼到一个独立接口中。 ![](https://box.kancloud.cn/2016-08-15_57b1b5e73208d.gif) **动机(Motivation)** classes 之间彼此互用的方式有若干种。「使用一个class 」通常意味覆盖该class 的所有责任区( whole area of responsibilities )。另一种情况是,某一组客户只使用class 责任区中的一个特定子集。再一种情况则是,class 需要与「所有可协助处理某些特定请求」的classes 合作。 对于后两种情况,将「被使用之部分责任」分离出来通常很有意义,因为这样可以使系统的用法更清晰,同时也更容易看清系统的责任划分。如果新的需要支持上述子集,也比较能够看清子集内有些什么东西。 在许多面向对象语言中,这种「责任划分」能力是通过多重继承(multiple inheritance)支持的。你可以针对一段行为(each segment of behavior )建立一个class ,再将它们组合于一份实现品(implementation)中。Java 只提供单一继承(single inher),但你可以运用interfaces (接口〉来昭示并实现上述需求。interfaces 对于Java 程序的设计方式有着巨大的影响,就连Smalltalk 程序员都认为interfaces (接口) 是一大进步! Extract Superclass 和Extract Interface 之间有些相似之处。Extract Interface只能提炼共通接口,不能提炼共通代码。使用Extract Interface 可能造成难闻的「重复」臭味,幸而你可以运用Extract Class 先把共通行为放进一个组件(component)中,然后将工作委托(delegating)该组件,从而解决这个问题。如果有不少共通行为,Extract Superclass 会比较简单,但是每个class 只能有一个superclass (译注:每个class 却能有多个interfaces )。 如果某个class 在不同环境下扮演截然不同的角色,使用interface (接口)就是个好主意。你可以针对每个角色以Extract Interface 提炼出相应接口。另一种可以用上Extract Interface 的情况是:你想要描述一个class 的外驶接口(outbound interface ),亦即这个class 对其server 所进行的操作〉。如果你打算将来加入其他种类的server ,只需要求它们实现这个接口即可。 **作法(Mechanics)** - 新建一个空接口(empty interface )。 - 在接口中声明「待提炼类」的共通操作。 - 让相关的胡实现上述接口。 - 调整客户端的型别声明,使得以运用该接口。 **范例:(Example)** TimeSheet class 表示「月报表」,其中将计算花在员工身上的费用。为了计算这笔费用,TimeSheet 需要知道员工级别,以及该员工是否有特殊技能: ~~~ double charge(Employee emp, int days) { int base = emp.getRate() * days; if (emp.hasSpecialSkill()) return base * 1.05; else return base; } ~~~ 除了提供员工的索费级别和特殊技能信息外,Employee 还有很多其他方面的功能,但本应用程序只需这两项功能。我可以针对这两项功能定义一个接口,从而强调「我 只需要这部分功能」的事实: ~~~ interface Billable { public int getRate(); public boolean hasSpecialSkill(); } ~~~ 然后,我声明Employee 实现这个接口 : ~~~ class Employee implements Billable ... ~~~ 完成以后,我可以修改charge() 函数声明,强调该函数只使用Employee 的这部分行为: ~~~ double charge(Billable emp, int days) { int base = emp.getRate() * days; if (emp.hasSpecialSkill()) return base * 1.05; else return base; } ~~~ 此刻,我们只不过是在文档化(documentability)方面获得了一些适度收获。对函 数,这样的收获并没有太大价值;但如果有若干classes 都使用Billable 接口,它就会很有用。如果我还想计算计算器租金,巨大的收获就显露出来了。为了让公司里的计算器都「能够被计费」(billable),我只需让Computer class 实现Billable 接口,然后就可以把计算器租金登记到月报表上了。