Interface Segregation Princeple(ISP 接口隔离原则)

一. What is ISP(Interface Segregation Princeple)

接口隔离原则,旨在确保接口的设计是专门化和细粒度的。根据ISP,一个类不应该被迫依赖于它不使用的方法或属性。
换句话说:宁愿拆成很多小接口,也不要搞一个超级大的胖接口。


二.ISP错误举例

1.1 串口温度传感器

非常臃肿的uart驱动接口,违反ISP原则。

// 超级大接口 - 所有模块都必须实现所有函数
typedef struct {
    void (*init)(void* self);
    void (*read)(void* self, uint8_t* buf, size_t len);
    void (*write)(void* self, const uint8_t* buf, size_t len);
    void (*flush)(void* self);
    void (*get_status)(void* self);
    void (*sleep)(void* self);
    void (*wakeup)(void* self);
    void (*set_baudrate)(void* self, uint32_t baud);
    void (*set_parity)(void* self, uint8_t parity);
    void (*ioctl)(void* self, int cmd, void* arg);   // 万能控制
} uart_ops;   // ← 非常胖的接口

使用时非常尴尬。

// 只想用作只读传感器的模块,却被迫实现一堆无关函数
typedef struct {
    uart_ops ops;
    // ...
} temperature_sensor;

void temperature_read(void* self, uint8_t* buf, size_t len) { ... }

void temperature_init(void* self) { ... }

// 下面这些传感器根本不需要,却被迫实现
void temperature_write(...)     { return; }     // 空实现
void temperature_flush(...)     { return; }
void temperature_sleep(...)     { return; }
void temperature_set_baudrate(...) { return; }
void temperature_ioctl(...)     { return; }     // 最痛苦的空实现

1.2 机器人

非常臃肿的worker接口,roboter被迫实现所有函数。

typedef struct {
    void (*work)(void* self);
    void (*eat)(void* self);
    void (*sleep)(void* self);
    void (*take_break)(void* self);
    void (*attend_meeting)(void* self);
} worker;   // ← 超级胖

void robot_eat(void* self)    { /* 机器人不吃饭 */ }
void robot_sleep(void* self)  { /* 机器人不睡觉 */ }
void robot_take_break(...)    { /* 无意义 */ }

三.ISP正确举例

3.1 串口温度传感器

划分为应用层和驱动层抽象接口, 按需实现。

// ---------- 温度传感器应用层抽象接口 ----------
typedef struct {
    void (*init)(void* sensor);
    int  (*read_temperature)(void* sensor, float* temp);
    void (*deinit)(void* sensor);
} temperature_ops;

// ---------- UART驱动层抽象接口 ----------
typedef struct {
    void (*init)(void* uart);
    int  (*write)(void* uart, const uint8_t* data, size_t len);
    int  (*read)(void* uart, uint8_t* buf, size_t len);
    void (*set_baud)(void* uart, uint32_t baud);
} uart_ops;

3.2 机器人

typedef struct { void (*work)(void*); }          IWorkable;
typedef struct { void (*eat)(void*); }           IEatable;
typedef struct { void (*sleep)(void*); }         ISleepable;
typedef struct { void (*take_break)(void*); }    IBreakable;

typedef struct {
    IWorkable*  workable;
    // 人类才有的
    IEatable*   eatable;
    ISleepable* sleepable;
} Human;

typedef struct {
    IWorkable* workable;
    // 机器人没有其他接口
} Robot;

3.3 函数指针数组

使用函数指针数组实现接口隔离。

typedef enum {
    INTERFACE_LIFECYCLE = 0,
    INTERFACE_READABLE,
    INTERFACE_WRITABLE,
    INTERFACE_POWER,
    INTERFACE_COUNT
} interface_type_e;

typedef struct {
    void* impl;
    void* vtable[INTERFACE_COUNT];
} interface_provider;

四.ISP和LSP

ISP的目的是把大的臃肿,功能不相关的接口拆成多个小接口,防止"客户端"被迫实现。


LSP的目的是使用组合、去掉不合理的继承关系。例如正方形继承自矩形,但是改width会连带改height;一般的鸟都可以飞,但是鸵鸟继承鸟不会飞,运行出错。


在实际编码中,“写小接口 + 谨慎使用继承” 是同时满足 ISP 和 LSP 最有效的一招。

posted @ 2026-02-23 18:17  自由的Charles  阅读(13)  评论(0)    收藏  举报