文章目录
单一职责原则(Single Responsibility Principle)
- 介绍
- 一个类只负责一项职责
- 如果A类有职责1和职责2,当职责1需求修改而改变A类时,可能导致职责2执行错误
- 所以需要A类的粒度分解为A1类、A2类
- 好处
- 能降低类的复杂度
- 能提高类的可读性和可维护性
- 降低方法变更引起的风险
- 注意
- 通常情况我们应当遵守单一职责原则
- 但如果逻辑足够简单,可以在代码级违反单一职责原则
- 如果类中方法数量足够少,可以在方法级别保持单一职责原则
- 即可以分成少个不同职业的方法
接口隔离原则(Interface Segregation Principle)
- 介绍
- 客户端(类)不应该依赖(实现)它不需要的接口(一个类对另一个类的依赖应该建立在最小接口上)
- 如果接口1中有5个方法,A类实现了接口1
- A类只需要使用接口1其中的3个方法
- 但此时A类实现接口1需要实现5个方法,此时违反了接口隔离原则
- 我们可以将接口1拆分成接口1和接口2,接口1包含3个方法,接口2包含另外2个方法
- 此时A类实现接口1则只需要实现3个方法,满足接口隔离原则
- 注意
- 接口隔离不一定是需要将5个方法拆分成5个接口,我们只需要满足最小接口
- 比如A类需要3个方法,B类需要另外2个方法,我们只需要将1个接口拆分成2个接口
依赖倒转(倒置)原则(Dependence Inversion Principle)
- 介绍
- 高层模块(类)不应该依赖低层模块(类),二者都应该依赖其抽象
- 抽象不应该依赖细节,细节应该依赖抽象
- 依赖倒转的中心思想是面向接口编程
- 基于以下设计理念
- 相对于细节的多变性,抽象的东西要更稳定
- 以抽象为基础搭建的架构比以细节为基础的架构更稳定
- java中,抽象指的是接口或抽象类,细节就是具体的实现类
- 使用接口或抽象类的目的是制定好规范,不涉及任何具体的操作,它们的实现类则负责展现各自的细节
- 依赖关系传递的三种方式
- 接口传递
- 构造方法传递
- setter传递 [setter(arg)]
- 注意事项和细节
- 低层模块(类)尽量要有抽象类/接口,或者两者都有,这样程序稳定性更好
- 变量的声明类型尽量是抽象类/接口,此时变量引用和实际对象之间就存在了一个缓冲层,有利于程序的扩展和优化
- 实际上就是接口作为声明的变量来new实现类
- B类实现了A接口,在main方法中创建B类
A b = new B();
- 继承时遵循里氏替换原则
- 低层模块(类)尽量要有抽象类/接口,或者两者都有,这样程序稳定性更好
- 个人理解
- 每个类都尽量实现接口/抽象类
- 在接口/抽象类中制定类的规范,细节则在类中进行编写
里氏替换原则(Liskov Substitution Principle)
- 里氏替换原则主要讲如何正确的使用继承
- 对继承的思考
- 继承包含这样一层含义:父类中凡是已经实现好的方法,实际上是在设定规范和契约,虽然它不强制要求所有的子类必须遵循这些契约,但是如果子类对这些已经实现的方法进行任意的修改,就会对整个继承体系造成破坏
- 继承在给程序设计带来便利的同时,也带来了弊端
- 使用继承会给程序带来侵入性,程序的可移植性降低,增加对象间的耦合性
- 如果A类被其它的类所继承,则当A类需要修改时,就必须考虑所有的子类,并且A类修改后,所有涉及到子类的功能都有可能产生故障
- 正因如此,我们应该如何正确的使用继承?里氏替换原则就是如何正确使用继承的指导原则
- 介绍
- 所有引用基类的地方必须能透明地使用其子类的对象
- 核心:在使用继承时遵循里氏替换原则,在子类中尽量不要重写父类的方法
- 里氏替换原则告诉我们,继承实际上让两个类的耦合性增强了,在适当的情况下我们可以通过聚合、组合、依赖关系来解决问题(在类中声明/使用另外的类的方法,而不用继承)
- 例子
- 违背里氏替换原则
- A类有1个方法,B类继承A类,实现了A类的方法;
- 但此时B类不小心重写了A类的方法(实际中可能是忘了曾经重写过该方法)
- B类新添加了1个方法使用到重写的方法
- 此时B类新添加的方法运行的结果并不是期待的结果
- 修改为符合里氏替换原则
- 将A、B类共同的基础的功能对应的方法提取出来,放到新的接口/抽象类中,我们假设该接口/抽象类为Base(此次例子方法比较少,因此不需要)
- A、B类都实现该接口/抽象Base,此时就可以写各自的方法了
- 如果B类需要用到A类的方法,则只需要在自身中声明A类(组合关系),通过A类的对象调用其方法即可
- 在B类中
A a = new A();
- 在B类中
- 违背里氏替换原则
开闭原则(ocp)(Open Closed Principle)
- 介绍
- 开闭原则是编程中最基础、最重要的设计原则
- 一个软件实体如类、模块和函数应该对扩展开放**(对提供方提供扩展),对修改关闭(对使用方关闭修改)**,用抽象构建框架,用实现扩展细节(类似依赖倒转)
- 当软件需要变化时,尽量通过扩展软件实体的行为来变化(添加类/模块/方法),而不是通过修改原有的代码实现变化
- 编程中遵循其它原则,以及使用设计模式的目的就是遵循开闭原则
- 个人理解
- 将不同类中共有的功能抽出到接口/抽象类中,具体其它细节则在类中实现
- 当我们做完功能后,如果需要添加其它功能,可以选择以下方式来增加其它功能,这就是扩展
- 添加类并实现接口
- 添加类并实现抽象类
- 添加方法
- 添加模块
- 如果使用了同一个接口,也可以利用多态的特性,用接口作为声明变量,实现类创建对象
A b = new b()
迪米特法则(Demeter Principle)
- 介绍
- 一个对象应该对其它对象保持最少的了解
- 类与类关系越密切,耦合度越大
- 迪米特法则也称为最少知道原则
- 一个类对自己依赖的类知道的越少越好。
- 对于被依赖的类不管多么复杂,都尽量将逻辑封装在类的内部
- 对外除了提供public的方法,不对外泄露任何信息
- 迪米特法则最简单的定义
- 只与直接朋友通信
- 什么是朋友关系?
- 首先需要了解耦合的方式:依赖、关联、组合、聚合等
- 每个对象都会与其它对象有耦合关系,只要两者之间有耦合关系,就称两者为朋友关系
- 什么是直接朋友?
- 如果出现成员变量、方法参数、方法返回值中的类为直接朋友
- A类中定义成员变量是B类对象
- A类的方法参数是B类对象
- A类方法的返回值是B类对象
- 出现在局部变量中的类不是直接朋友
- 在方法中创建其它类的对象,在A类方法的方法体中创建B类的对象
B b = new B();
- 例外情况:如果B类对象是A类方法的参数或返回值,说明是直接朋友
- 在方法中创建其它类的对象,在A类方法的方法体中创建B类的对象
- 陌生的类最好不要以局部变量的形式出现在类的内部,这样就不是直接朋友(违反迪米特法则)
- 如果出现成员变量、方法参数、方法返回值中的类为直接朋友
- 细节
- 迪米特法则的核心是降低类之间的耦合
- 由于每个类都减少了不必要的依赖,因此迪米特法则只是要求降低 类/对象 之间的耦合关系,并不是要求完全没有依赖关系
- 个人理解
- 例子理解
- 如果A类方法中需要用到B类的对象,并且B类对象也不是A类方法的参数/返回值,此时就不是直接朋友
- 此时我们就可以将B类对象封装到某个类的方法中(该方法与B类对象是直接朋友)
- 迪米特法则重点
- 降低类之间的耦合度
- 了解直接朋友
- 减少不是直接朋友的类,达到降耦合(将不是直接朋友的类封装到 其它 是该类的直接朋友的 类/方法中)
- 例子理解
合成复用原则(Composite Reuse Principle)
核心:尽量使用合成/聚合的方式,而不是继承
设计原则核心思想
- 找出可能需要变化之处,将它们独立出来,不要和不需要变化的代码相混。
- 面向接口编程,而不是针对实现编程
- 为了交互对象之间的松耦合设计而努力(降低对象之间的耦合)