现在的位置: 首页 > 综合 > 正文

软件设计原则—-接口隔离原则(ISP)

2013年12月08日 ⁄ 综合 ⁄ 共 1546字 ⁄ 字号 评论关闭

“使用多个专门的接口比使用单一的总接口要好”。

“一个类对另外一个类的依赖性应该建立在最小的接口上”。

陈述:

  • 不应该强迫客户依赖于他们不用的方法。
  • 一个类的不内聚的“胖接口”应该被分解成多组方法,每一组方法都服务于一组不同的客户程序。
例子:
 Door可以加锁、解锁、而且可以感知自己是开还是关;
 Door是抽象基类,客户程序可以依赖于抽象而不是具体的实现。

class Door{
	public:
		virtual void Lock( )=0;
		virtual void Unlock( )=0;
		virtual bool IsDoorOpen( )=0;
};

现在我们要增加一个功能:如果门打开时间过长,它就会报警(比如宾馆客房的门)。
设计:为了实现上述新增功能,我们要求Door与一个已有的Timer对象进行交互。

  • 如果一个对象希望得到超时通知,它可以调用Timer的Register函数。
  • 该函数有两个参数,一个是超时时间,另一个是指向TimerClient对象的指针,此对象的TimerOut函数会在超时时被调用。
class Timer{
public:
	void Register(int timeout,TimerClient* client);
};

class TimerClient{
public:
	virtual void TimerOut( );
};

问题出来了:我们如何将TimerClient和TimedDoor联系起来?

一种常见的解决方案如下:
这种解决方法的问题——接口污染
在Door接口中加入新的方法(Timeout),而这个方法仅仅只为它的一个子类带来好处。——如果每次子类需要一个新方法时它都被加到基类接口中,基类接口将很快变胖。
胖接口将导致SRP,LSP被违反,从而导致脆弱、僵化。
解决:
我们使用委托的方式来解决,见下图所示:
Class TimedDoor:public Door{
public:
	virtual void DoorTimeOut(int timeOutID);
};


class DoorTimeAdapter:public TimerClient{
	TimedDoor& itsTimedDoor;
public:
	DoorTimerAdapter(TimedDoor& theDoor):itsTimedDoor(theDoor){}
	vitual void TimeOut(int timeOutId){
		itsTimedDoor.DoorTimeOut(timeOutId);
	}
};

解决(续):
另一种解决办法是使用多继承:

Class TimedDoor:public Door, public TimerClient{
public:
	virtual void DoorTimeOut(int timeOutID);
};

注意:多重继承的方法,要慎用!在Effective C++中,已经提到了这点。

总结:

  • 准确而恰当地划分角色以及角色所对应的接口,是面向对象设计的一个重要组成部分;
  • 每个接口都代表一个角色,实现一个接口的对象,因此将角色区分清楚是系统设计的一个重要工作。一个符合逻辑的推断,不应将几个不同的角色都交给同一个接口,而应交给不同的接口。
  • 将没有关系的接口合并在一起,形成一个臃肿的大接口,是对角色和接口的污染。

相应设计模式
Memento
Iterator

参考资源:

《设计模式:可复用面向对象软件的基础》,ERICH
GAMMA RICHARD HELM RALPH JOHNSON JOHN VLISSIDES著作,李英军 马晓星 蔡敏 刘建中译,机械工业出版社,2005.6

《敏捷软件开发:原则、模式与实践》,Robert
C. Martin著,邓辉译,清华大学出版社,2003.9

《设计模式解析》,Alan
Shalloway等著(徐言声译),人民邮电出版社,2006.10

抱歉!评论已关闭.