Skip to the content.

接口分离原则

在开发一个系统的时候,通过设计减少系统里功能之间的耦合来提高质量。 一个好的方式就是把类分组到包中,并控制好他们之间的依赖。你可以根据下面的规则告诉类怎么在一个包中调用其他的类 — — 例如,在领域层的类可能不会调用在显示层包中的类。

然而,你也许需要调用与一般结构相冲突的方法。如果是这样,使用接口隔离在包中定义一个接口,但是在另一个包实现它。在客户端使用这种方式就需要依赖这个接口,并且它完全不知道它的实现。对于网管接口隔离原则提供了一个好的组件。

案例

一般情况下,我们在设计类与类之间的关系会像下面这样:

public class Ops {
	public void Op1() {
	}
	
	public void Op2() {
	}
	
	public void Op3() {
	}
}

public class User1 {
	Ops op1;
	void Do() {
		op1.Op1();
	}
}
public class User2 {
	Ops op2;
	void Do() {
		op2.Op2();
	}
}
public class User3 {
	Ops op3;
	void Do() {
		op3.Op3();
	}
}

这段代码设计有什么问题呢?

首先 User 三个类都直接依赖类 Ops,虽然这三个类各自用 Ops 不同的方法,但是由于都共同依赖一个实现类 Ops,当你只修改 Ops.Op2() 方法时,虽然其他两个不受影响,但是也同样由于 Op2 代码更改要重新编译发布。更严重会面临代码意料之外的更改的风险,特别是在 Ops 类内部细节可能存在共享变量的情况。

如果我们采用接口分离原则来重新设计上面那段代码 — — 把类 Ops 中的三个方法分别抽象成三个接口,然后用一个类来实现这个三个接口,那么我们的实体 User 只需要分别依赖继承对应的接口即可,不需要关注其具体实现:

interface IUser1Ops {
    void Ops1();
}
interface IUser2Op2 {
    void Ops2();
}
interface IUser3Op3 {
    void Ops3();
}

class User1 {
    User1(IUser1Ops ops)
    void Do() {
        ops.Ops1();
    }
}
class User2 {
    ...
    void Do() {
        ops.Ops2();
    }
}
class User3 {
    ...
    void Do() {
        ops.Ops3();
    }
}

public Ops : IUser1Ops,IUser2Ops,IUser3Ops {
    void Ops1(){}
    void Ops2(){}
    void Ops3(){}
}

我们可以看到,User 不直接依赖于具体实现类,而是更上一层的抽象类。这样一来,当更改具体实现类是,我们修改 Ops 具体的业务逻辑代码,根本不需要重新编译和部署 User 部分的代码。

https://martinfowler.com/eaaCatalog/separatedInterface.html