python 面向对象原则

背景

编码解决实际问题,但代码主要是给人看的。面向对象的代码人比较容易理解,所以大型项目基本采用面向对象的编码方式。面向对象怎么做,怎样才算做得好呢,就需要 SOLID 原则来评判,满足原则的就好,不满足原则的就需要修改。

SOLID 原则

单一职责原则 Single responsibility principle

影响类修改的因素应该只有一个,如果有多个,将类拆分。或将类的方法拆分成外部函数。

开闭原则

类或函数对修改封闭,对扩展开放。你应该可以在不修改类的前提下,扩展类的行为。
例如 sorted 函数,sorted([1,3,2]),sorted([1,3,2],key=lambda i : i %2),通过参数扩展类的功能。
实现开闭原则有三种方法

  1. 使用继承,找到父类中会变动的部分,将其抽象成新的方法(或属性),最终允许新的子类来重写它以改变类的行为。
  2. 依赖注入(Dependency injection) 是解决这个问题的另一种思路。与继承不同,依赖注入允许我们在类实例化时,通过参数将业务逻辑的变化点:帖子过滤算法 注入到类实例中。
  3. 将经常变动的东西,完全以数据的方式抽离出来。当需求变动时,只改动数据,代码逻辑保持不动。它的原理与“依赖注入”有一些相似,同样是把变化的东西抽离到类外部。不同的是,后者抽离的通常是类,而前者抽离的是数据。
    1.

里氏替换原则 L

当你使用继承时,子类(派生类)对象应该可以在程序中替代父类(基类)对象使用,而不破坏程序原本的功能。
user 作为父类,有禁用方法,admin 作为子类,不能被禁用。编写批量禁用用户函数时,循环禁用 user,就会报错,因为 user 里可能有 admin 类型用户。admin 不能被 user 替换使用,违反里氏替换原则。
解决方法:
子类不能只是简单通过抛出异常的方式对某个类方法进行“退化”。如果 “对象不能支持某种操作” 本身就是这个类型的 核心特征 之一,那我们在进行父类设计时,就应该把这个 核心特征 设计进去。

  • 子类方法应该和父类同名方法返回同一类型,或者返回支持更多操作的子类型也行

  • 子类的方法参数应该和父类同名方法完全一致,或者更为宽松

接口隔离原则 Interface Segregation Principles

一个接口所提供的功能,应该是使用方所需要的方法,不多不少刚刚好。

  • 设计接口就是设计抽象

  • 违反接口隔离原则也可能会导致违反单一职责与里式替换原则

  • 写更小的类、写更小的接口在大多数情况下是个好主意

依赖倒置原则 Dependency Inversion Principle

“高层模块不应该依赖于低层模块,二者都应该依赖于抽象。”如果直接依赖于低层模块,回导致过度耦合,单元测试出问题。
image.png
转变为
image.png
**

参考

https://mp.weixin.qq.com/s?__biz=Mzg2NjExNDI0MQ==∣=2247483697&idx=1&sn=16b858a2827348d21c8796d6343c5561&chksm=ce4e8e9ff9390789f900598cd95ac78e9aeed77945ec81a409bcda0aaea5aa42e8da9f85f13d&mpshare=1&scene=1&srcid=0915SiOIjQi6Qq1DCOFCc8p8&sharer_sharetime=1600133232973&sharer_shareid=6f4376b2af3fc7e23cc803eb85b120ac&version=3.0.30.2006&platform=win#rd
https://mp.weixin.qq.com/s?__biz=Mzg2NjExNDI0MQ==∣=2247483702&idx=1&sn=35a2640b5a3d0314533a6be4a5db3c16&chksm=ce4e8e98f939078ef639f7bfbe0a12c28a81bc75f9c2316f785da2cf7a8400e64a9533ea1e4e&mpshare=1&scene=1&srcid=0915cvb4I4zB8M3XYsHLpxJq&sharer_sharetime=1600136940144&sharer_shareid=6f4376b2af3fc7e23cc803eb85b120ac&version=3.0.30.2006&platform=win#rd