SOLID的ISP
ISP(接口隔离原则)
定义:
- 客户端不应该依赖它不需要的接口
- 类间的依赖关系应该建立在最小的接口上
举例说明(为了保持突出主题,实例代码并不完善,且没有考虑异常),
某天之前,小王设计I接口,用于业务处理, 有部分用户需要计费,有部分不需要,但均使用I接口。
class I {
public:
virtual ~I();
virtual bool charge() = 0;
virtual void proc() = 0;
};
class A:public I
{
public:
A(string const &account);
virtual bool charge(); // if charged successfully, then return true;
virtual void proc();
private:
string _account;
};
class B:public I
{
public:
B(string const &account);
virtual bool charge(); // directly return true;
virtual void proc();
private:
string _account;
};
class Service
{
public:
void do(string const &account)
{
I *p = NULL;
// 130 用户要收费
if ( 0 == account.compare(0,3,"130") )
{
p = new A(account);
}
// 非130用户免费
else
{
p = new B(account);
}
if (!p->charge())
{
return;
}
p->proc();
}
};
后来用户提出需求,需要对扣费了的用户,生成话单记录。
对于A类用户,在charge()方法中,记录话单即可。
而对于B类用户,不需做任何调整。
从这一例来看,ISP并没有发挥多大的作用。
因此,ISP并不是任何时刻都必须使用。要深入分析,找到他到底解决什么样的根本问题,才是最重要的。
本例中,由于Service类中业务逻辑非常简单,因此未看出有何不妥。
但如果客户变更需求,对于A类用户需要在计费结束的时候,通知用户扣费的结果。那么我们把Service::do稍作修改,
void Service::do(string const &account)
{
I *p = NULL;
...
if (!p->charge())
{
//发送通知消息,表明扣费失败
return -1;
}
else
{
//发送通知消息,表明扣费成功
}
p->proc();
}
这样,对于B类用户来说,charge()接口无论改为返回true还是false都不能避免消息下发,这样,在没有扣费的情况下仍然下发了消息。
虽然,可以通过:
1.在Service::do里面特殊处理,2.在B::charge()中修改类似全局变量的方式来通知Service::do在后面特殊处理,或者修改接口
来避免业务逻辑错误。但本身带来了诸多出错的机会。
因此,从本例来看,问题出现在A类用户是要计费的,B类是不计费的。而接口公用charge(),也就是计费接口。
因此,服务是依赖计费这个接口来进行实现的。所以,上下文会根据计费成功或者计费失败来设计上下文,对于根本
没有计费的情况,就难以覆盖了。除非,对于计费的结果不需要有任何业务逻辑来处理,那么就没有多大的影响。
所以,如果不是业务专家,对于需求还在澄清阶段,请严格遵循ISP。

浙公网安备 33010602011771号