单一职责原则
冲鸭! 先来学一学编程七大原则.. -- 降耦合、增加可复用性、可扩展性.
# 单一职责原则
简单来说, 就是我们编写一个类, 这个类是干什么的, 要清晰, 不要让它身兼多职.. 要避免大而全.
还有一点, 并不是粒度划分的越细越好, 应以业务需求为判断依据, 合适的才是最好的..
举个栗子, 电脑的信息:
-1- 类Laptop你把它理解成一个接口 (抽象,继承它的类要实现这些东西), 类BasicLaptop实现了这个接口..
-2- 将类Laptop作了一个拆分, 其中的属性变成了接口LaptopProperty, 其中的方法也变成了接口并进行了实现..
-3- 属性还可以根据业务需求进一步划分, 通用的属性和格外的属性, 属性接口再拆分成两个接口.. 方法同理.
# ★开闭原则
简单来说, 就是对扩展开发, 对修改关闭..
说白了就是在需求发生变化的时候.我们要在不修改源代码的基础上,扩展我们的组件.
Why? 有啥好处.
1> 扩展满足需求的变动后,只需对扩展的代码进行测试, 原来的测试代码是能够正常运行的..
2> 设计的力度小, 复用的可能性就高. 增加了代码的复用性、稳定性.
举个栗子:
左侧发邮箱、发短信, 现在新增一个发微信, 需要改动源代码, 重新写一下if-else.. 不妥. 右侧就很好的解决了..
# 迪米特原则
简单来说, 一个类对于其它类知道的越少越好. 只和直接朋友进行通信. 不提倡与非直接朋友进行联系.
该原则在一定程度上避免了通过一系列的对象链来访问深层次的属性.
迪米特法则的应用: 门面模式、中介模式.
每个对象都会与其他对象之间都存在一定程度的耦合关系, 其中主要耦合方式包含: 泛化、关联、聚合、组合、实现、依赖.
基于这些耦合关系, 类与类之间就可以成为朋友, 那什么情况下是直接朋友呢?
为了降低耦合性, 我们规定 成员变量、方法参数、方法返回值中的类, 可认为是 类/类对象 的直接朋友..
类/类对象 最好与自己的直接朋友进行交互.. 从而降低耦合性.
~_^ 示例, 客户冲浪去商店买滑板, 商店 charge_customer 向客户收费..
根据上面示例, 我们知道迪米特法则, 不希望 objectA.getObjectB.doSomething();
当然, 更不希望 objectA.getObjectB.getObjectC.doSomething();
思考:
1. 上述示例通过迪米特法则进行优化后,客户具体付款的方式也不会被限制了,除了卡支付,还可以现金支付、微信支付等.
那如何编写代码,使得在新增支付方式后,只需改动pay方法,不改动SurfShop类呢? (后面学了设计模式再回头来看
2. 在上述示例中,商户只关心钱的事情,也就是关心card的操作,客户更像是一个中介,在这个简单的示例里,这样设计没有问题.
★★★ 但如果滥用迪米特法则,那么系统中会存在大量的中介类!!
那这些类呢它存在只是为了传递类之间的调用关系,在一定程度上呢会增加系统的复杂度. (后面学习设计模式时再深入探讨
2
3
4
5
# 依赖倒置原则
高层和低层都基于抽象, 底层/细节/具体类 负责实现抽象.
这使得当下层的实现细节不断变动时,只要我们的抽象不变,上层就不需要变化.
这大大降低了上层模块和底层实现细节之间的耦合度, 软件结构会更稳定,也更灵活!!
依赖倒置的应用: 工厂模式
~_^ 示例, 买车, BMW宝马、Mercedes奔驰、Honda本田.
注意: 开闭原则强调扩展不修改, 依赖倒置原则强调不依赖低层, 这两个原则的实现 都需要 接口/抽象..
# 合成复用原则
合成是指 对象之间耦合关系中的 聚合/组合.
继承(也叫泛化)、聚合、组合 都能帮我们实现软件复用.
合成复用原则提倡我们尽量使用 聚合或者组合来达到软件复用的效果.
为什么呢? 先来弄懂这三种耦合关系的含义.
耦合关系 | ||
---|---|---|
继承 (is-A) | 新的类能够吸收已有类的属性和行为,并拓展新的能力 | 动物 - 猫、狗 |
聚合 (has-A) | 子组件可以独立于父组件而存在 ~_^ 大难临头各自飞 | 公司与销售员、工程师 学校与学生 图书馆与书 |
组合 (contains-A) | 子元素不能独立于父元素而存在 ~_^ 共进退,同生死 | 身体与手、脚 房子与房间 |
☆ 在聚合关系中,整体和部分之间,在生命周期上没有什么必然的联系 部分对象可以在整体对象创建之前创建,也可以在整体对象销毁之后销毁
☆ 在组合关系中,部分和整体共存亡,整体的生命周期结束,也意味着部分的生命周期结束
接下来, 我们来比较下 继承-聚合-组合 的优缺点.
◎ 继承复用: 优点-简单容易实现 ; 缺点-破坏了封装性、耦合高、不够灵活.
◎ 合成复用: 优点-封装性好、耦合度低、够灵活 ; 缺点-有较多的对象需要管理.
- 子类继承父类之后,这个父类的所有功能,都可以通过继承关系进入子类,因而这种关系啊很容易实现;
- 父类和子类的耦合度高,父类的任何改变,都会导致子类的实现发生变化,这不利于类的扩展和维护
- 使用继承会将父类实现的细节暴露给子类,那这种复用称为白箱子复用.
继承关系中父类对子类是透明的,没有隐藏父类的实现细节,也就是说继承破坏父类的封装性(隐藏实现细节,仅对外提供访问接口).
★ 合成复用:将 [已有的对象] 纳入 [新的对象] 中,使之成为新对象的一部分,那新对象可以调用已有对象的这个功能
- 它维持了这个类的封装性,因为已有对象的内部细节是新对象看不见的,所以这种复用也称之为黑箱复用
- 通过合成复用建造的系统,有较多的对象需要管理,但这个缺点在好的设计下往往是可以被规避的.
○ 聚合和组合 在python中 通常使用初始化方法 __init__来实现.
def __init__(self, make, horsepower):
self.make = make # 聚合
self.engine = Engine(horsepower) # 组合
2
3
4
5
6
7
8
9
10
11
12
13
综上, 继承复用和合成(聚合/组合)复用, 我们更推荐合成复用. 因为它 封装性好、耦合度低、提高了代码灵活性.
接下来, 看个栗子, 这个例子跟造车子有关.
记住一个公式 聚合和组合 > 继承
后续很多设计模式会直接套用这个公式, 帮助我们更好的理解如何应用这个原则.
# 接口隔离原则
接口隔离原则是 单一职责原则 的好兄弟. 不用太纠结于这两个原则有何不同, 大胆点 就是一样的.(´▽`)
接口隔离原则,简单来说,就两点:
-1- 客户端不应该依赖它不用的接口 - 若依赖了不需要的接口,就得承担不需要的接口变动所带来的风险.低耦合
-2- 类之间的依赖应该建立在最小接口上.. 即 多个专门的接口 > 单一的总接口. 高内聚
提醒一点: 我们默认实现接口后,应该重写里面的所有方法.. 下面python代码中所谓的接口是没有强制约束的.. 别较真.
# 里氏替换原则
遵循里氏替换原则来规范父类和子类的编写, 未来这些代码的出错率会变低.
里氏替换原则(LSP) 包含4点:
-1- 子类必须完全实现父类的抽象方法, 但不能覆盖父类的非抽象方法.
-2- 子类可以实现自己特有的方法.
-3- 当子类的方法实现父类的抽象方法时, 方法的后置条件要比父类更严格.
-4- 子类的实例可以替代任何父类的实例, 但反之不成立 - 一句话, 任何基类可以出现的地方, 子类一定可以出现.
七大编程原则, 我自己的理解, 不保真哈.emmm
>> 开闭原则、依赖倒置原则
- 高层-抽象-底层 开闭原则强调扩展不修改, 依赖倒置原则强调高层不依赖低层, 这两个原则的实现 都需要 接口/抽象..
>> 单一职责、接口隔离
- 大接口拆分成几个小接口
>> 合成复用原则、里氏替换原则
- 将 [已有的对象] 纳入 [新的对象] 中,使之成为新对象的一部分,那新对象可以调用已有对象的这个功能
类与类之间的关系 聚合、组合 > 继承
- 父类和子类之间编码规范
>> 迪米特原则
- 不提倡与非直接朋友进行联系, 不希望objectA.getObjectB.doSomething();
《图解设计模式》《大话设计模式》Github上搜索设计模式相关项目.
2
3
4
5
6
7
8
9
10
11
12
13
14
15
基于这七大原则的23种设计模式.
设计模式针对面向对象系统中重复出现的设计问题, 提出一个通用的设计方案, 并予以系统化的命名和动机解释.