前言:
迪米特原则也称为最少知道原则,即一个软件实体需尽可能少地与其他实体发生相互作用。类与类之间的关系密切,代码的耦合度就越大,扩展和复用就越难。如果两个类之间没有必要的直接通信,那么这两个类之间就不应该发生直接交互,可以通过合理的引入第三方来解耦这两个类之间的关系。
概述
一个类对另一个类应知道的越少越好,尽量降低类中成员变量及成员函数的访问权限,就相当于不把相应的变量和方法暴露给其他类,这样可以尽量少地影响其他类或模块,这样的代码扩展起来会相对容易。所以迪米特原则有一个更直白的定义——不要和陌生人说话。类应设计为只与直接的朋友通信,那么哪些类,对象,变量可以成为直接的朋友呢?只要是两个类之间的耦合关系,就可以说这两个对象是朋友关系。耦合关系包括:继承,实现,依赖,关联,聚合,组合等。
当前类对象的直接朋友包括:
- 当前类对象的本身
- 当前类对象创建的实例对象
- 当前类对象的成员对象
- 当前类对象的实例所引用的对象
- 以参数形式传入到当前类对象方法中的对象
满足上面任一条件的对象都是当前对象的"直接朋友";否则就是陌生人。迪米特原则强调了以下两点:
- 从依赖者的角度:只依赖应该依赖的对象。
- 从被依赖者的角度:只暴露应该暴露的方法或属性,即编写相关的类时确定方法和属性的权限(public or private)
场景1
张三去找李四帮忙做一件事,对于李四来说这件事也很难办,但是李四的朋友王五却能完成。所以李四就把这件事交给王五去办。(注意:在本例中张三和王五是不认识的)。现在我们暂定张三为A,李四为B,王五为C。写出以下不符合 迪米特原则的代码:
class A:
def __init__(self, name):
self.name = name
def get_b(self, name):
return B(name)
def work(self):
b = self.get_b("李四")
c = b.get_c("王五")
c.work()
class B:
def __init__(self, name):
self.name = name
def get_c(self, name):
return C(name)
class C:
def __init__(self, name):
self.name = name
def work(self):
print("%s 把这个事情做好了" % self.name)
if __name__ == "__main__":
a = A("张三")
a.work()
上面的代码输出结果是符合期望的。“王五确实是把事情办妥了”,但是我们仔细看代码逻辑确发现这样做不对的,因为张三和王五我们已经预设了互相不认识的前提,那为什么代表张三的A类中会有代表王五的C类呢?所以这也的代码不符合迪米特原则。现在我们要对上面的代码进行重构,根据迪米特原则——从依赖者的角度来看,只依赖应该依赖的对象。在本例中张三只认识李四,那么只能依赖李四,重构后代码如下:
class A:
def __init__(self, name):
self.name = name
def get_b(self, name):
return B(name)
def work(self):
b = self.get_b("李四")
b.work()
class B:
def __init__(self, name):
self.name = name
def get_c(self, name):
return C(name)
def work(self):
c = self.get_b("王五")
c.work()
class C:
def __init__(self, name):
self.name = name
def work(self):
print("%s 把这个事情做好了" % self.name)
if __name__ == "__main__":
a = A("张三")
a.work()
场景2
当我们按下计算机的关机按钮的时候,计算机会进行一系列操作如:保存当前任务,关闭相关服务,接着关闭显示器,最后关闭电源。这些操作完成则计算机才算关闭。根据此场景我们可以写出如下不符合迪米特原则的代码:
class Computer:
def save_task(self):
print("任务保存了")
def close_service(self):
print("服务关闭了")
def close_screen(self):
print("屏幕关闭了")
def close_power(self):
print("电源关闭了")
def close(self):
self.save_task()
self.close_service()
self.close_screen()
self.close_power()
class Person:
def __init__(self, c=Computer()):
self.c = c
def clickCloseButton(self):
self.c.save_task()
self.c.close_power()
self.c.close()
# 或者
self.c.close_power()
# 或者
self.c.close()
self.c.close_screen()
if __name__ == "__main__":
p = Person()
p.clieckCloseButton()
对于计算机的使用者来说,期待的结果只是按下关闭按钮后计算机就关掉,而不是需要去小心的保存当前正在执行的任务等。在上面的代码中,类Computer 中的方法是完全公开的,对于类 Person 来说,手里面就如同多出了好几把钥匙,至于具体用那一把他不知道,所以要挨个去试一遍,显然这样的设计是不对的。所以根据迪米特原则——从被依赖者的角度来看,只暴露应该暴露的方法。在本例中实际应该暴露的方法就是 close()。关于计算机的其他操作不是依赖者应该关注的问题,应该对依赖者关闭。重构后代码如下:
class Computer:
def _save_task(self):
print("任务保存了")
def _close_service(self):
print("服务关闭了")
def _close_screen(self):
print("屏幕关闭了")
def _close_power(self):
print("电源关闭了")
def close(self):
self._save_task()
self._close_service()
self._close_screen()
self._close_power()
class Person:
def __init__(self, c=Computer()):
self.c = c
def clickCloseButton(self):
self.c.close()
if __name__ == "__main__":
p = Person()
p.clickCloseButton()
使用迪米特原则注意事项:
- 在不违反需求的情况下尽量降低每个类中成员变量和成员函数的访问权限
- 合理引入第三方降低对象之间直接交互的耦合度
- 过度应用迪米特原则会产生大量的代理类或中介类,导致系统过于复杂。所以需要权衡使用,使得代码既满足高内聚低耦合,又能够做到结构清晰。
网友评论