接口隔离原则,Interface Segregation Principle,ISP
01 May 2023
夫道未始有封,言未始有常,為是而有畛也。請言其畛:有左,有右,有倫,有義,有分,有辯,有競,有爭,此之謂八德。
《庄子·齐物论》
软件工程领域,有一个概念叫 SOLID。SOLID 是五个面向对象设计原则的首字母缩写。最早由 Robert C. Martin 在其 2000 年的论文《Design Principles and Design Patterns》中提出的。
其中,I 原则指的是 Interface Segregation Principle,接口隔离原则,简称 ISP。
ISP 是这么表述的:“Clients should not be forced to depend upon interfaces that they do not use.” 不应该强迫对象的用户依赖于它不需要的接口。也就是说,为了使用某个功能,客户/用户不应该被强制使用超出该功能的接口。那么,要避免这种情况,就需要把过大的接口拆分成粒度更小的接口。
比如我们要设计一个闹钟,闹钟其实有多种功能:可以读取当前时间,可以设置闹铃时间。
class AlarmClock { public: time_t readTime() const; void setAlarm(time_t); };
然后,我们又定义了一个 TimeReporter,它其实只关心时间,它并不需要知道怎么设置闹铃时间。
class TimeReporter { public: void reportTime(AlarmClock& c) { announceTime(c.readTime()); } };
另一个,AlarmSetter 用来设置闹钟闹铃:
class AlarmSetter { public: void setClockAlarm(AlarmClock& c, time_t& t) { c.setAlarm(t); } };
这就导致了 AlarmSetter 和 TimeReporter 依赖所有 AlarmClock 的接口。 AlarmClock 的任意一个接口改动,都会影响前两者的实现。
ISP 原则就要求 AlarmClock 应该实现两个接口,分别是 Clock 和 Alarm,这样两个不同功能的接口就分离开互相不影响。
class Clock { public: virtual time_t readTime() const; }; class Alarm { public: virtual void setAlarm(time_t); }; class AlarmClock : public Clock, public Alarm { public: time_t readTime() const; void setAlarm(time_t); }; class TimeReporter { public: void reportTime(Clock& c) { announceTime(c.readTime()); } }; class AlarmSetter { public: void setClockAlarm(Alarm& a, time_t& t) { a.setAlarm(t); } };