敏捷开发原则与实践(七)之接口隔离原则_项目管理_非技术区_程序员俱乐部

中国优秀的程序员网站程序员频道CXYCLUB技术地图
热搜:
更多>>
 
您所在的位置: 程序员俱乐部 > 非技术区 > 项目管理 > 敏捷开发原则与实践(七)之接口隔离原则

敏捷开发原则与实践(七)之接口隔离原则

 2013/12/18 9:09:06  飞雪COCO  博客园  我要评论(0)
  • 摘要:接口隔离原则(TheInterface-SegregationPrinciple)强调类的功能要单一,类的功能臃肿增加不必要的耦合,增加代码的脆弱性,还会增加编译依赖。该原则建议将方法分组,达到隔离接口的目的,具体的方法有委托和多重继承。在TimedDoor的例子中,开始的实现是TimedDoor继承Door,Door实现TimerClient这个接口,这样TimedDoor就可以通过TimerClient这个接口去使用Timer的功能。但这样做会导致Door依赖于TimerClient
  • 标签:敏捷开发 开发 接口 敏捷

接口隔离原则(The Interface-Segregation Principle)强调类的功能要单一,类的功能臃肿增加不必要的耦合,增加代码的脆弱性,还会增加编译依赖。该原则建议将方法分组,达到隔离接口的目的,具体的方法有委托和多重继承。

在Timed Door的例子中,开始的实现是Timed Door继承Door,Door实现Timer Client这个接口,这样Timed Door就可以通过Timer Client这个接口去使用Timer的功能。但这样做会导致Door依赖于Timer Client,而且并不是所有的Door都需要定时功能。所有的继承于Door的子类都需要提供退化的Timeout方法,违反了LSP。

注意,这里的TimerClient是Timer实现的需求,与Door无关。所有使用Timer的类对相当于Timer的client,需要实现TimerClient这个接口,用于Timer time out时调用。

clip_image002

Timer Client at Top of Hierarchy

Door和Timer Client中的接口分别由不同的client使用,既然使用者相互独立,接口也应该独立,因为使用者也会对接口产生影响。我们一般都会考虑接口对使用者的影响,但使用者也会对接口提出更多的要求。比如文中由于door的需求,需要Timer提供带timeOutId的注册接口。但这个接口的变化会导致Door的所有子类代码都需要修改,这个设计的确很糟糕。

接口隔离原则:使用者不应该被逼去依赖它们不使用的方法。

Clients should not be forced to depend on methods that they do not use.

通过委托或多重继承可以避免依赖不使用的方法。一般使用多重继承,因为委托会产生多余的性能和空间的耗用。这里的委托就是说将对TimerClient的实现委托给TimerClientAdapter去做。

TimedDoor例子中通过将定时接口和Door接口隔离,避免了其它Door子类依赖于定时接口。

 clip_image004

Multiply inherited Timed Door

 

clip_image006

Door Timer Adapter

class="code_img_closed" src="/Upload/Images/2013121809/0015B68B3C38AA5B.gif" alt="" />logs_code_hide('ed93d3a8-415d-40d0-bbec-5ed8005be704',event)" src="/Upload/Images/2013121809/2B1B950FA3DF188F.gif" alt="" />
 1 public interface TimerClient {
 2     public void timeOut();
 3 }
 4 
 5 public class Timer2 extends TimerTask{
 6 
 7     private TimerClient mTc;
 8     private Timer mTimer;
 9     
10     public void registry(int timeout, TimerClient tc) {
11         mTc = tc;
12         mTimer = new Timer();
13         mTimer.schedule(this, timeout*1000);
14     }
15 
16     @Override
17     public void run() {
18         // TODO Auto-generated method stub
19         mTc.timeOut();
20     }
21 }
Timer以及接口
 1 public class TimedDoor2 extends Door implements TimerClient {
 2 
 3     public TimedDoor2(int second) {
 4         super();
 5         Timer2 t = new Timer2();
 6         t.registry(second, this);
 7     }
 8 
 9     public void doorTimeOut () {
10         if (!mIsLock) {
11             lock();
12         }
13     }
14     
15     @Override
16     public void timeOut() {
17         // TODO Auto-generated method stub
18         doorTimeOut();
19     }
20 
21 }
TimerDoor多重继承实现
 1 public class DoorTimerAdapter implements TimerClient {
 2     private TimedDoor mTimedDoor;
 3     
 4     public DoorTimerAdapter(TimedDoor td) {
 5         mTimedDoor = td;
 6     }
 7 
 8     @Override
 9     public void timeOut() {
10         // TODO Auto-generated method stub
11         mTimedDoor.doorTimeOut();
12     }
13 
14 }
15 
16 public class TimedDoor extends Door {
17     
18     public TimedDoor() {
19         super();
20         Timer2 t = new Timer2();
21         t.registry(5, new DoorTimerAdapter(this));
22     }
23 
24     public void doorTimeOut () {
25         if (!mIsLock) {
26             lock();
27         }
28     }
29 
30 }
31 
32 public class Timer2 extends TimerTask{
33 
34     private TimerClient mTc;
35     private Timer mTimer;
36     
37     public void registry(int timeout, TimerClient tc) {
38         mTc = tc;
39         mTimer = new Timer();
40         mTimer.schedule(this, timeout*1000);
41     }
42 
43     @Override
44     public void run() {
45         // TODO Auto-generated method stub
46         mTc.timeOut();
47     }
48 }
49 
50 public interface TimerClient {
51     public void timeOut();
52 }
TimerDoor委托实现

在这里说一下对接口的理解。之前对接口的使用一直局限在多态的使用上,即定义一个接口,加上若干实现,所有定义对象的地方可以使用接口定义,运行时创建不同的对象。因为这个局限,导致自己一直无法理解抽象类和接口到底有什么区别,觉得抽象类和接口是可以互换的。

仔细想想,接口定义的是行为。一个类实现一个接口,说明它具备这些行为能力,比如TimedDoor实现Timer Client接口后,说明它具备定时功能。一个类可以具备很多行为能力,还有多个类会具备相同的行为能力,比如你也可以开发一个实现Timer Client接口的TimedCar类,它也具备定时功能。在这里,就需要使用接口来将各种各样的行为进行分类,类需要什么功能,就实现什么样的接口。

抽象类中的定义的方法也可以称为行为,它与接口定义的行为有什么区别呢?抽象类是对类的抽象,接口是对行为的抽象。每个类都会有自己的一些基本行为,它是在我们对现实世界进行抽象时得到的,比如Door肯定具备open、close行为。同时各个类之间又会具备一些不同的行为,我们不能保证,不可能所有类具备同样的行为,这时就需要接口来作为补充。刚开始往往认为代码中有抽象类和接口才是面向对象编程,这是错误的。滥用抽象类和接口会增加程序的复杂度,我们应该只在需要时才进行重构使用抽象类和接口

TimedDoor例子中是该类调用的接口需要隔离,即Door需要实现的功能太多了,非TimedDoor没必要实现TimerClient;ATM机的例子中则是UI中被Trasaction调用的接口需要分类隔离,即UI提供的功能太多了。

ATM需要在多种终端上实现不同类型的UI,比如盲文、触摸屏、按键等,这就要求UI的实现需要非常flexible。相反,ATM后台事务逻辑很固定,几乎不会变化。因此就把UI对象传递给事务,让事务去请求UI做事情,比如要求用户输入等。以前,我们一般都会认为UI去调用事务逻辑,这里刚好相反,很巧妙!

但之前将所有的UI行为(存钱UI、取钱UI等)放在一个接口中,任何一个Transaction发生变化时,如果影响了UI接口,那么就会将这种变化传递给其它Transaction,因为它们都依赖于UI。最典型的就是当增加一个Transaction时,UI中必须增加一个接口,这时就会导致所有Transaction重新编译。

clip_image008

ATM Transaction Hierarchy

image

Segregated ATM UI Interface

最后,当接口发生变化时,为了防止由此导致的大规模重新编译和重新部署,可取的方法是新增接口,而不是修改原有的接口。

 

上一篇: Model First:创建实体数据模型(ADO.NET 实体数据模型) 下一篇: 没有下一篇了!
发表评论
用户名: 匿名