纯净、安全、绿色的下载网站

首页|软件分类|下载排行|最新软件|IT学院

当前位置:首页IT学院IT技术

Python编程应用设计原则 Python编程应用设计原则详解

somenzz   2021-09-14 我要评论
想了解Python编程应用设计原则详解的相关内容吗somenzz在本文为您仔细讲解Python编程应用设计原则的相关知识和一些Code实例欢迎阅读和指正我们先划重点:Python编程,Python应用设计原则下面大家一起来学习吧

写出能用的代码很简单写出好用的代码很难

好用的代码也都会遵循一此原则这就是设计原则它们分别是:

  • 单一职责原则 (SRP)
  • 开闭原则 (OCP)
  • 里氏替换原则 (LSP)
  • 接口隔离原则 (ISP)
  • 依赖倒置原则 (DIP)

提取这五种原则的首字母缩写词就是 SOLID 原则下面分别进行介绍并展示如何在 Python 中应用

1、单一职责原则 SRP

单一职责原则(Single Responsibility Principle)这个原则的英文描述是这样的:A class or module should have a single responsibility如果我们把它翻译成中文那就是:一个类或者模块只负责完成一个职责(或者功能)

让我们举一个更简单的例子我们有一个数字 L = [n1, n2, …, nx] 的列表我们计算一些数学函数例如计算最大值、平均值等

一个不好的方法是让一个函数来完成所有的工作:

import numpy as np
 
def math_operations(list_):
    # Compute Average
    print(f"the mean is {np.mean(list_)}")
    # Compute Max
    print(f"the max is {np.max(list_)}") 
 
math_operations(list_ = [1,2,3,4,5])
# the mean is 3.0
# the max is 5

实际开发中你可以认为 math_operations 很庞大揉杂了各种功能代码

为了使这个更符合单一职责原则我们应该做的第一件事是将函数 math_operations 拆分为更细粒度的函数一个函数只干一件事:

def get_mean(list_):
    '''Compute Max'''
    print(f"the mean is {np.mean(list_)}") 
def get_max(list_):
    '''Compute Max'''
    print(f"the max is {np.max(list_)}") 
def main(list_): 
    # Compute Average
    get_mean(list_)
    # Compute Max
    get_max(list_)
main([1,2,3,4,5])
# the mean is 3.0
# the max is 5

这样做的好处就是:

  • 易读易调试更容易定位错误
  • 可复用代码的任何部分都可以在代码的其他部分中重用
  • 可测试为代码的每个功能创建测试更容易

但是要增加新功能比如计算中位数main 函数还是很难维护因此还需要第二个原则:OCP

2、开闭原则 OCP

开闭原则(Open Closed Principle)就是对扩展开放对修改关闭这可以大大提升代码的可维护性也就是说要增加新功能时只需要添加新的代码不修改原有的代码这样做即简单也不会影响之前的单元测试不容易出错即使出错也只需要检查新添加的代码

上述代码可以通过将我们编写的所有函数变成一个类的子类来解决这个问题代码如下:

import numpy as np
from abc import ABC, abstractmethod
class Operations(ABC):
    '''Operations'''
    @abstractmethod
    def operation():
        pass
class Mean(Operations):
    '''Compute Max'''
    def operation(list_):
        print(f"The mean is {np.mean(list_)}") 
class Max(Operations):
    '''Compute Max'''
    def operation(list_):
        print(f"The max is {np.max(list_)}") 
class Main:
    '''Main'''
    def get_operations(list_):
        # __subclasses__ will found all classes inheriting from Operations
        for operation in Operations.__subclasses__():
            operation.operation(list_)
if __name__ == "__main__":
    Main.get_operations([1,2,3,4,5])
# The mean is 3.0
# The max is 5

如果现在我们想添加一个新的操作例如:median我们只需要添加一个继承自 Operations 类的 Median 类新形成的子类将立即被 __subclasses__()接收无需对代码的任何其他部分进行修改

3、里氏替换原则 (LSP)

里式替换原则的英文是 Liskov Substitution Principle缩写为 LSP这个原则最早是在 1986 年由 Barbara Liskov 提出他是这么描述这条原则的:

If S is a subtype of T, then objects of type T may be replaced with objects of type S, without breaking the program

也就是说 子类对象能够替换程序中父类对象出现的任何地方并且保证原来程序的逻辑行为不变及正确性不被破坏

实际上里式替换原则还有另外一个更加能落地、更有指导意义的描述那就是按照协议来设计子类在设计的时候要遵守父类的行为约定(或者叫协议)父类定义了函数的行为约定那子类可以改变函数的内部实现逻辑但不能改变函数原有的行为约定这里的行为约定包括:函数声明要实现的功能;对输入、输出、异常的约定;甚至包括注释中所罗列的任何特殊说明

4、接口隔离原则 (ISP)

接口隔离原则的英文翻译是 Interface Segregation Principle缩写为 ISPRobert Martin 在 SOLID 原则中是这样定义它的:Clients should not be forced to depend upon interfaces that they do not use

直译成中文的话就是:客户端不应该被强迫依赖它不需要的接口其中的 客户端 可以理解为接口的调用者或者使用者

举个例子:

from abc import ABC, abstractmethod
class Mammals(ABC):
    @abstractmethod
    def swim(self) -> bool:
        pass
    @abstractmethod
    def walk(self) -> bool:
        pass
class Human(Mammals):
    def swim(self)-> bool:
        print("Humans can swim")
        return True 
    def walk(self)-> bool:
        print("Humans can walk")
        return True 
class Whale(Mammals):
     def walk(self) -> bool:
        print("Whales can't walk")
        return False
     def swim(self):
        print("Whales can swim")
        return True 
human = Human()
human.swim()
human.walk()
whale = Whale()
whale.swim()
whale.walk()
 

执行结果:

Humans can swim
Humans can walk
Whales can swim
Whales can't walk

事实上子类鲸鱼不应该依赖它不需要的接口 walk针对这种情况就需要对接口进行拆分代码如下:

from abc import ABC, abstractmethod
class Swimer(ABC): 
    @abstractmethod
    def swim(self) -> bool:
        pass
class Walker(ABC):
    @abstractmethod
    def walk(self) -> bool:
        pass
class Human(Swimer,Walker):
    def swim(self)-> bool:
        print("Humans can swim")
        return True
    def walk(self)-> bool:
        print("Humans can walk")
        return True
class Whale(Swimer):
    def swim(self):
        print("Whales can swim")
        return True
human = Human()
human.swim()
human.walk()
whale = Whale()
whale.swim()

5、依赖反转原则 (DIP)

依赖反转原则的英文翻译是 Dependency Inversion Principle缩写为 DIP英文描述:High-level modules shouldn't depend on low-level modules. Both modules should depend on abstractions. In addition, abstractions shouldn't depend on details. Details depend on abstractions

我们将它翻译成中文大概意思就是:高层模块不要依赖低层模块高层模块和低层模块应该通过抽象(abstractions)来互相依赖除此之外抽象不要依赖具体实现细节具体实现细节依赖抽象

在调用链上调用者属于高层被调用者属于低层我们写的代码都属于低层由框架来调用在平时的业务代码开发中高层模块依赖低层模块是没有任何问题的但是在框架层面设计的时候就要考虑通用性高层应该依赖抽象的接口低层应该实现对应的接口如下图所示:

也就是说本来 ObjectA 依赖 ObjectB但为了扩展后面可能会有 ObjectCObjectD经常变化因此为了频繁改动让高层模块依赖抽象的接口 interface然后让 ObjectB 也反过来依赖 interface这就是依赖反转原则

举个例子wsgi 协议就是一种抽象接口高层模块有 uWSGIgunicorn等低层模块有 DjangoFlask 等uWSGIgunicorn 并不直接依赖 DjangoFlask而是通过 wsgi 协议进行互相依赖

依赖倒置原则概念是高层次模块不依赖于低层次模块看似在要求高层次模块实际上是在规范低层次模块的设计低层次模块提供的接口要足够的抽象、通用在设计时需要考虑高层次模块的使用种类和场景明明是高层次模块要使用低层次模块对低层次模块有依赖性现在反而低层次模块需要根据高层次模块来设计出现了「倒置」的显现

这样设计好处有两点:

  • 低层次模块更加通用适用性更广
  • 高层次模块没有依赖低层次模块的具体实现方便低层次模块的替换

最后的话

我去年(2020)年 2 月 3 号购买的《设计模式之美》专栏将近一年半才把它学习完再回看之前的代码真是一堆垃圾之前一天写完的代码重构差不多花了一个星期重构之后感觉还可以再重构的更好似乎无止境正如小争哥说的那样项目无论大小都可以有技术含量所谓代码细节是魔鬼细节到处都存在在取舍应该怎么样不应该怎么样都大有学问


相关文章

猜您喜欢

  • mongodb清除连接和日志 mongodb清除连接和日志的正确方法分享

    想了解mongodb清除连接和日志的正确方法讲解的相关内容吗考古学家lx在本文为您仔细讲解mongodb清除连接和日志的相关知识和一些Code实例欢迎阅读和指正我们先划重点:mongodb清除连接,mongodb清除日志,mongodb,日志清理下面大家一起来学习吧..
  • Selenium+Python自动化脚本环境 Selenium+Python自动化脚本环境搭建的全过程

    想了解Selenium+Python自动化脚本环境搭建的全过程的相关内容吗chen793991833在本文为您仔细讲解Selenium+Python自动化脚本环境的相关知识和一些Code实例欢迎阅读和指正我们先划重点:selenium+python自动化框架,selenium自动化框架,python+selenium自动化测试框架实例下面大家一起来学习吧..

网友评论

Copyright 2020 www.sopisoft.net 【绿软下载站】 版权所有 软件发布

声明:所有软件和文章来自软件开发商或者作者 如有异议 请与本站联系 点此查看联系方式