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);
    }
};