小知识,大挑战!本文正在参与“程序员必备小知识”创作活动
本文同时参与 「掘力星计划」 ,赢取创作大礼包,挑战创作激励金
如果看过前面我写的4种原则,那接口隔离原则理解起来就不难,它和前面的那些原则有点类似,但又有点不一样,这会在下面有所阐述。不知道为什么,每次学了一些设计原则,都有点想撸代码的冲动。
定义
接口隔离原则:指明客户不应该被迫依赖于对其而言无用的方法或功能。一个类对另一个类的依赖应该建立在最小的接口上。
也就会说接口隔离原则要求程序员尽量将臃肿庞大的接口拆分成更小的和更具体的接口,让接口中只包含客户感兴趣的方法。每个类都有自己的专用的接口,不要视图建立一个很庞大的接口提供给所有依赖它的类去实现。
对于swift来说,接口就相当于协议。
重点
接口隔离原则和单一职责原则是有点类似,都是为了提高类的内聚性、降低它们之间的耦合性。区别的是单一职责原则重点的是职责,约束的是类,每个类都有自己的职责,而接口隔离原则重点的是接口,更注重的是对接口依赖的隔离。
接口隔离原则目的是系统解开耦合,从而容易重构,更改和重新部署。
那我们应该怎么做呢
- 每个接口只服务于相应的模块和业务。
- 只提供调用者需要的方法,不需要的方法不提供。
- 当一个接口太大时,我们需要将它分割成一些更细小的接口。
- 每个接口需承担相对独立的角色,不要把不同的角色都交给1个接口。
- 每个项目或产品都有选定的环境因素,环境不同,接口拆分的标准就不同。
优点
-
将臃肿庞大的接口分解为多个粒度小的接口,可以预防外来变更的扩散,提高系统的灵活性和可维护性。
-
接口隔离提高了系统的内聚性,减少了对外交互,降低了系统的耦合性。
-
如果接口的粒度大小定义合理,能够保证系统的稳定性;但是,如果定义过小,则会造成接口数量过多,使设计复杂化;如果定义太大,灵活性降低,无法提供定制服务,给整体项目带来无法预料的风险。
-
使用多个专门的接口还能够体现对象的层次,因为可以通过接口的继承,实现对总接口的定义。
-
能减少项目工程中的代码冗余。过大的大接口里面通常放置许多不用的方法,当实现这个接口的时候,被迫设计冗余的代码。
例子
例子我就直接用公司聘用员工来做一个Demo,假设我们有一个IT协议,里面包含写代码,写文档,管理的方法。
/// IT协议
protocol ITProtocol {
/// 写代码
func writeCode()
/// 写文档
func writeDocument()
/// 管理
func manage()
}
我们有一个码农的类,和项目经理的类,都遵循IT协议。
//码农
class Programmer: ITProtocol {
func writeCode() {
print("writeCode")
}
func writeDocument() {
print("writeDocument")
}
func manage() {
print("not manage")
}
}
//项目经理
class ProjectManager: ITProtocol {
func writeCode() {
print("not writeCode")
}
func writeDocument() {
print("writeDocument")
}
func manage() {
print("manage")
}
}
这时候我们有一个公司的类,公司想聘用员工,就通过ITProtocol协议来聘用。
/// 公司
class Company {
/// 招聘
/// - Parameter it: 具备某种技能的人才
func employ(it: ITProtocol) {
it.writeCode()
it.writeDocument()
it.manage()
}
}
公司想聘用一个码农,调用输出代码:
var company = Company()
var programmer = Programmer()
company.employ(it: programmer)
输出结果:
writeCode
writeDocument
not manage
公司想聘用一个项目经理,调用输出代码:
var company = Company()
var projectManager = ProjectManager()
company.employ(it: projectManager)
输出结果:
not writeCode
writeDocument
manage
表面上我们是实现了功能,但是我们又感觉有些类,输出了一些不想要的方法。公司雇佣的码农类Programmer是不需要管理manage()方法的,项目经理类ProjectManager是不需要写代码writeCode()的。
那在我们OC中,我们可以用optional来让哪些方法是可选的,这是一种做法。但要是方法很多,我们还是需要对协议进行拆分。
我们开发协议的就是要写代码功能的。管理协议就是要管理功能的。
我们就可以这么设计:
/// IT协议
protocol ITProtocol {
/// 写文档
func writeDocument()
}
/// 开发协议
protocol DevelopmentProtocol: ITProtocol {
/// 写代码
func writeCode()
}
/// 管理协议
protocol ManageProtocol: ITProtocol {
/// 管理
func manage()
}
弄了3个协议,抽象父类IT协议,开发协议,管理协议。我们如果是仅有开发需要的功能就在开发协议里面实现,仅有管理需要功能就在管理里面实现,如果开发管理都用到的,我们就在IT协议实现。
这时候,我们只要让码农类Programmer遵循DevelopmentProtocol协议,项目经理类ProjectManager遵循ManageProtocol协议,就可以保证只需要实现自己的相关的功能即可。
//码农
class Programmer: DevelopmentProtocol {
func writeCode() {
print("writeCode")
}
func writeDocument() {
print("writeDocument")
}
}
//项目经理
class ProjectManager: ManageProtocol {
func writeDocument() {
print("writeDocument")
}
func manage() {
print("manage")
}
}
我们调用代码:
var company = Company()
var programmer = Programmer()
company.employ(devIt: programmer)
var projectManager = ProjectManager()
company.employ(manageIt: projectManager)
输出结果:
writeDocument
writeCode
writeDocument
manage
这样子,我们就相当于自己类关联自己的方法,无关的方法就不会再关联了。通过划分更细的协议来实现,这就是接口隔离原则。
当然,这里还有继续优化的地方,这里写文档功能是如果都是一样做的,我们还可以这么写
extension ITProtocol {
/// 写文档
func writeDocument() {
print("writeDocument")
}
}
这取决于功能的输出内容,如果写文档功能虽然大家都有,但是每个实现这个功能的是有区别的,我们还是在相应的类实现就好。
写到这里,我觉得还是再补充一个小例子优化吧,毕竟项目中经常会遇到,假设有一个TestClass类,里面有testA,testB,testC方法,我们有另外一个类,调取TestClass对象,需要testA为true,再调取testB,testB为true,再调取testC来返回结果。
class TestClass {
func testA() -> Bool { true }
func testB() -> Bool { true }
func testC() -> String { "成功"}
}
func test() {
let t = TestClass()
if t.testA() {
if t.testB() {
t.testC()
}
}
}
我们这么实现有没有问题,好像是没有问题,实际上我这个test方法,只想要一个结果,那对于testA,testB,testC是不在乎的,那我们完全就可以封装在TestClass实现。
class TestClass {
private func testA() -> Bool { true }
private func testB() -> Bool { true }
private func testC() -> String { "成功"}
func testABC () -> String {
if testA() {
if testB() {
return testC()
}
}
return ""
}
}
func test() {
let t = TestClass()
t.testABC()
}
这样子我们的testA,testB,testC是不是就不用暴露出去了呢,好处的是后面testA,testB,testC有要改动的,我仅需要检查testABC()这个方法否有受到影响就行。
这就是提高内聚,减少对外交互。使接口用最少的方法去完成最多的事情。
总结
我们虽然说要拆分接口尽量小和具体,但是也是有限度的。对接口进行细化可以提高程序设计灵活性,但是如果过小,则会造成接口数量过多,使设计复杂化。所以一定要适度。
欢迎在评论区讨论,掘金官方将在掘力星计划活动结束后,在评论区抽送100份掘金周边,抽奖详情见活动文章