美文网首页
依赖倒置原则

依赖倒置原则

作者: 木叶苍蓝 | 来源:发表于2023-02-26 11:40 被阅读0次

    前言:
    每个逻辑的实现都是由原子逻辑组成的,不可分割的原子逻辑称为低层模块,在 Python 中用抽象类来表示,由原子逻辑组装而成的就是高层模块,抽象类是不能被实例化的。细节就是实现类,通过继承抽象类而产生的类就是细节,是可以被直接实例化的。一言以蔽之,采用依赖倒置原则可以减少类间的耦合性,提高系统的稳定性,提高代码的可读性和可维护性。依赖倒置原则包含如下三层含义:
    1、高层模块(由原子逻辑组装而成)不应该依赖低层模块(不可分割的原子逻辑),二者都应该依赖抽象
    2、抽象(抽象类)不应该依赖细节(实现类)
    3、细节应该依赖抽象

    依赖倒置原则究竟倒置在哪里?

    “倒置”指的是和一般面向对象的代码的设计思考方式完全相反。举个例子,假设现在有建设一个汽车城,首先会想到是这里会卖很多品牌的汽车。如奔驰,宝马,奥迪,沃尔沃等。汽车城是上层模块,汽车是下层模块。如图所示:


    modb_20211009_35bbc080-2858-11ec-abb4-fa163eb4f6be.jpg

    利用上图模块间依赖关系设计的代码,当每新增一个品牌的汽车(低层模块),汽车城(高层模块)就多了一个新的依赖;汽车城(高层模块)依赖所有品牌的汽车(低层模块),因为它直接创建汽车;对于不同品牌的汽车(低层模块)具体实现的任何改变都会影响到汽车城(高层模块),这就违背了依赖倒置原则。根据依赖倒置原则,高层模块不应该依赖低层模块,两者都应该依赖抽象。也就是说汽车城这个高层模块是不应该依赖汽车品牌这个低层模块,所以代码设计思路应该从下层模块开始思考并对其进行抽象。如下图所示:


    modb_20211009_35f5287a-2858-11ec-abb4-fa163eb4f6be.jpg

    这样就很好的解释了 “上层模块不应该依赖底层模块,它们都应该依赖于抽象”这一概念,在最开始的设计中,高层模块(汽车城)直接依赖低层模块(不同品牌的车),调整设计后高层模块和低层模块都依赖于抽象(小汽车)。

    依赖倒置原则应用

    先不考虑依赖倒置原则,假设有如下类图所描绘的场景


    modb_20211009_36267114-2858-11ec-abb4-fa163eb4f6be.jpg

    由以上类图所描绘的场景可知,司机类和奔驰类都属于细节,并没有实现或继承抽象。它们是对象级别的耦合。通过类图可知司机类 Driver 有一个用来开车的 drive() 方法,Benz 类有一个表示车辆运行的 run() 方法,并且奔驰车类 Benz 依赖于司机类 Driver,用户模块 Client 是一个高层模块,负责调用司机类 Driver 和奔驰类 Benz。未采用依赖倒置原则的代码如下:

    class Driver:
        def __init__(self, name):
            self.name = name
    
        def drive(self, benz):
            print("%s is driving a benz" % self.name)
            benz.run()
    
    class Benz:
        def __init__(self, name = 'benz'):
            self.name = name
    
        def run(self):
            print("%s is running ..." % self.name)
    
    class BMW:
        def __init__(self, name="bmw"):
            self.name = name
    
        def run(self):
            print("%s is running..." % self.name)
    
    if __name__ == "__main__":
        d = Driver("张三")
        benz = Benz()
        d.drive(benz)
        bmw = BMW()
        d.drive(bmw)
    
    

    这是最不灵活的写法,如果驾驶员的车库中新增了一辆 bmw,新车无法上路。继续修改代码如下:

    class Driver:
        def __init__(self, name):
            self.name = name
    
        def drive(self, car):
            print("%s is driving a %s" % (self.name, car.name))
            car.run()
    
    class Benz:
        def __init__(self, name="benz"):
            self.name = name
        
        def run(self):
            print("%s is running ..." %self.name)
    
    class BMW:
        def __init__(self, name="bmw"):
            self.name = name
    
        def bmw_run(self):
            print("%s is running ..." % self.name)
    
    if __name__ == "__main__":
        d = Driver("张三")
        benz = Benz()
        d.drive(benz)
        print("#" * 20)
        bmw = BMW()
        d.drive(bmw)
    

    以上代码中,如果 benz 或 bmw 的运行方法不是 run,那么将会报错。此时需要将 Car 类的run方法进行抽象,来对子类进行约束。上面代码的设计没有使用依赖倒置原则,我们已经郁闷的发现,模块与模块之间耦合度太高,生产力太低,只有需求一变就会面临代码的大面积重构。现在引入依赖倒置原则,重新设计的类图如下:


    modb_20211009_372f38ca-2858-11ec-abb4-fa163eb4f6be.jpg
    from abr import ABCMeta, abstractmethod
    
    class Driver:
        __metaclass__ = ABCMeta
    
        def __init__(self, name):
            self.name = name
    
        @abstarctmethod
        def drive(self, car):
            pass
    
    class Car:
        __metaclass__ = ABCMeta
        def __init__(self, name):
            self.name = name
    
        @abstractmethod
        def run(self):
            pass
    
    class BenzCar(Car):
        def run(self):
            print("%s is running ..." % self.name)
    
    class BmwzCar(Car):
        def run(self):
            print("%s is running ..." % self.name)
    
    class CarDrive(Driver):
        def drive(self, car):
            print("%s is driving a car ..." % self.name)
            car.run()
    
    if __name__ == "__main__":
        rs = CarDrive("张三")
        bz = BenzCar("奔驰")
        bm = BmwCar("宝马")
    
        rs.drive(bz)
        rs.drive(bm)
    
    

    相关文章

      网友评论

          本文标题:依赖倒置原则

          本文链接:https://www.haomeiwen.com/subject/tdazkdtx.html