设计原则
封装变化
- 找出应用中可能需要变化之处,将其独立出来,不要和那些不需要变化的代码混在一起。
针对接口编程,而不是针对实现编程
多用组合,少用继承
尽量采用松耦合的设计
类应该对扩展打开,对修改关闭
依赖倒置
- 要依赖抽象,而非依赖具体类
- 使用方针:
- 变量不可以持有具体类的引用
- 不要让类派生自具体累
- 不要覆盖基类中已实现的方法
最少指导原则
- 只和你的密友谈话
- 目的:不要让太多的类耦和在一起,避免修改系统中一部分会影响到其他部分
- 使用方针:在该对象的方法哪我们应该调用属于以下范围的方法
- 该对象本身
- 被当作方法的参数而传递进来的对象
- 该方法所创建或实例化的任何对象
- 对象的任何组件
- 若一个对象是调用其他方法返回的结果,则不应该调用该对象(1~3总结获得;这样做相当于向另一个对象的子部分发送了请求,从而增加了我们直接认识的对象数目)
好莱坞原则
- 别调用我们,我们会调用你
- 只能高层组件调用底层组件
单一职责
- 一个类只有一个因其变化的原因
设计模式
策略模式 Strategy
- 示例:Duck - FlyBehavior/QuackBehavior
- 实现:定义算法族,分别封装起来,让他们之间可以互相替换。
- 目的:将行为(算法)的变化独立于使用行为(算法)的客户
- 理解:
- 将某一种行为抽象为接口(算法族),使用不同的方式(算法)实现该行为(接口)。在实际使用中动态设置对应的行为(算法),并进行[委托]调用。
- 将行为作为对象封装起来,实现多态
观察者模式 Observer
- 示例:天气预报站
- 实现:定义了对象之间一对多的依赖,这样一来,当一个对象改变状态时,他的所有依赖着都会收到通知并自动更新
- JDK实现
- java.util.Observer
- java.util.Observable
- 数据传输方式
- 观察者接到信息后,通过主题提供的方法(如:getXXX()等方法)拉取相关数据:Observable.notifyObservers()
- 主题主动推送:Observable.notifyObservers(Object)
- 理解:
- 主题(Subject)维护一个观察者列表,在状态改变时循环调用观察者(Observer)的通知方法
- 观察者实现主题相匹配的接口,以使主题可以调用到相关通知方法
- 观察者调用主题的注册/注销方法,更新主题中的观察者列表
装饰者模式 Decorator
- 示例:星巴克菜单
- 实现:动态地将责任附加到对象上
- 优点:若要扩展功能,装饰者提供了比继承更有弹性的替换方案
- 缺点:常常造成设计中存在大量的小类
- 特点:
- 装饰者与被装饰者有相同的超类(达到类型匹配的目的,而非获得相应的行为)
- 装饰者(有且仅有一个被包装的类实例)可以有一个或多个
- 可以用装饰之后的对象代替被装饰者
- 装饰者可以在被装饰者行为前后加上自己的行为,以达到特定的目的
- 对象可以在任何时候被装饰
- 理解:
- 装饰者/被装饰者(基础对象 or 装饰者)均需实现统一的超类以达成类型匹配
- 装饰者中存在一个超类的实例,该实例实即为被装饰对象
- 调用装饰者的方法时,调用被装饰对象相应的方法,且前后可添加自己的方法以达到装饰被装饰者逻辑的目的
工厂模式 Factory
- 示例:pizza
- 理解:
- 简单工厂模式/工厂方法模式/抽象工厂模式是相互独立的
- 简单工厂将实例创建的过程提取到单独的类中,达到复用的目的
- 工厂方法模式是将主流程抽象,并开放生成相关操作对象实例的方法;在具体实现类中使用具体操作对象的实例。
- 抽象工厂模式是生成单独的工厂类接口,接口中定义相关实例的生成方法;由具体工厂类实现接口。(常使用工厂方法模式实现)
简单工厂模式
- 并非是真正的设计模式
- 作用:处理对象创建的细节
- 目的:封装对象的创建
工厂方法模式
- 定义:定义了一个创建对象的接口,但由子类决定要实例化的类是那个。工厂方法让类把实例化推迟到了子类。
- 实现:通过让子类决定改创建的对象是什么,达到将对象创建的过程封装的目的。
- 实现方式:继承
抽象工厂模式
- 定义:提供一个接口,用于创建相关或依赖对象的家族(一组实例对象),而不需要明确制定具体类
- 实现方式:对象组合
工厂方法 | 抽象工厂 | |
---|---|---|
实现方式 | 继承 | 组合 |
使用场合 | 创建单一产品 | 创建产品组 |
缺点 | 单一 | 加入新产品,需要修改所有实现 |
单例模式 Singleton
- 示例:巧克力工厂
- 定义:确保一个类只有一个实例
- 注意:需要定义一个全局访问点
命令模式 Command
- 示例:卡槽遥控器
- 定义:将请求封装为对象,以便使用不同的请求/队列/日志来参数化其他对象。命令行模式同样支持可撤销操作。
- 优点:将动作的请求者(遥控器)从动作的执行者(电器)中解耦
- 理解: 将具体方法的调用委托给第三方,而调用者只需要统一向第三方发起命令
- 设计一个接口(Command),该接口需要实现某些方法(execute())
- 实现Command接口,接口方法一般为一组动作执行者的动作;并且保存动作的执行者
- 调用者只需要执行接口实例的execute方法即可
- 实际使用:
- 队列请求:队列中只需执行对应的接口方法,而不需要了解动作的执行者
- 日志请求:接口中新增store()/load();调用时使用store存储日志,若需要回复则使用load
适配器模式 Adapter
- 示例:火鸡伪装成鸭子
- 定义:将一个类的接口,转换成客户期望的另一个接口。
- 优点:适配器让原本接口不兼容的类可以合作无间
- 作用:将一个接口转换成另一个接口,以符合客户的期望
- 实现流程
- 创建实现 目标接口(鸭子) 的适配器类
- 在类中引入 被适配接口(火鸡)
- 使用被适配接口的方法实现目标接口的方法
- 理解:实现目标接口,并用被适配接口的方法实现所有目标接口的方法
外观模式 Facade
- 示例:家庭影院组
- 定义:提供一个统一的接口,用来访问子系统中的一群接口。外观定义了一个高层接口,让子系统更容易使用。
- 优点:
- 简化复杂的子系统功能
- 将客户与子系统进行解耦
模板方法模式 Template
- 示例:咖啡与茶的冲泡
- 定义:在一个方法中定义了一个算法的骨架,而将一些步骤延迟到子类中。
- 有点:模版方法是的子类可以在不改变算法结构的情况下,重新定义算法中的默写步骤
- 理解:
- 模版方法模式将算法进行了抽象,由子类决定如何实现算法中的步骤;策略模式则是封装了客户换的行为,并进行动态的选择
- 定义一个算法结构的抽象类,并将算法结构的方法保护起来(不允许子类进行修改)
- 在抽象中实现公共的部分
- 在具体实现中实现差异部分
- 该模式的重点在于提供一个算法,并让具体的类实现该算法中的某些方法
- 实际使用:
- 钩子函数(客户是否需要加料)
- 排序(排序方法为模板方法,由于为静态实现,所以通过继承Comparable保证重写了compareTo方法)
迭代器模式 Iterator
- 示例:两份菜单的整合遍历
- 定义:提供了一种方法顺序访问一个聚合对象中的各个元素,而不是暴露其内部实现
- 理解:用一个实现封装对象遍历所需要的方法
- 定义一个接口,其中有haveNext/next等方法
- 为实体类创建一个迭代类实现该接口
- 在实体类中添加方法,生成对应的迭代类
组合模式 Composite
- 示例:包含子菜单的菜单整合
- 定义:允许将对象组合成树形结构来表现 整体/部分 层次结构
- 组合:组件的集合
- 组件:基本类,包含组合和叶子结点
- 叶子结点:最具体的实体类
- 优点:
- 让客户以一致的方式处理个别对象以及对象组合
- 大多数情况可以忽略对象组合和个别对象之间的差别
- 理解:
- 创建组件接口,该接口组合及叶子结点均需要实现(操作的基本单位)
- 组合一般实现 增加 删除 获取 功能
- 叶子结点一般实现 基本操作 如:获取名称等
- 组合及叶子结点 可能会均实现一些方法,这些方法可以提供忽略组合及叶子结点的优势 如:execute
状态模式 State
- 示例:糖果机售卖糖果
- 定义:允许对象在内部状态改变时改变他的行为,对象看起来好像修改了他的类。
- 理解:
- 创建状态接口,接口中包含所有的操作
- 实现状态接口,实现方法,对该状态下所有的动作作出相应的相应
- 在状态所属的对象中维护当前状态,在进行操作时,实际上操作的是当前状态对象中实现的方法
代理模式 Proxy
- 示例:糖果机数据统计
- 定义:为另一个对象提供一个替身或占位符以控制对这个对象的访问
- 实际应用
- 远程代理 —— 网络访问
- 虚拟代理 —— 作为创建开销大的对象的代表
- 保护代理 —— 使用Java动态代理实现,
复合模式
- 定义:结合两个或以上的模式,组成一个解决方案,解决一再发生的一般性问题
MVC
- 策略模式 视图 - 控制器
- 观察者模式 模型 - 视图
- 组合模式 视图
桥接模式 Bridge
- 示例:一个遥控器控制多种电视
- 作用:不止改变你的实现,也改变你的抽象
- 理解:组合
- 将抽象(遥控器)与实现(不同品牌的电视)分别进行再抽象
- 用户只需要调用抽象
- 抽象中含有实现的接口,具体操作通过实现的接口操作
- 优点:抽象与实现可以各自扩展
- 应用:提供对外接口,而内部实现改动频繁或更换
生成器模式 Builder
- 示例:度假安排
- 定义:封装产品的构造过程,并允许按步骤进行构造
- 优点:提供一种方式来创建复杂的结构,而不会和创建他的步骤混合在一起
- 理解:流水线
- 创建生成器实例
- 通过生成器中的方法创建对象中所需的实例
- 通过生成器获取对象
责任链模式 Chain of Responsibility
- 示例:邮件处理
- 优点:让一个以上的对象有机会能够处理某个请求
- 应用:
- event冒泡
- 工作流
- 理解:
- 实现上:与装饰器模式类似
- 功能上:责任器模式偏向于传递,装饰器模式偏向于扩展
蝇量(享元) Flyweight
- 示例:种树
- 侧重点:轻量
- 作用:让某个类的一个实例能用来提供多个虚拟实例
- 理解:用一个实例中的Collection对象替代多个实例
解释器 Interpreter
- 示例:儿童编程
- 作用:为语言创建解释器
- 理解:分析简单的语法规则,并进行转变执行
- 定义语法
- 根据语法创建表达式对象(均继承相同的Expression接口);对象中维护不变部分
- 执行expression.interpreter(可变部分)
- 应用:spEL
中介者 Mediator
- 示例:智能家居
- 作用:集中相关对象之间复杂的沟通和控制方式
- 理解:将各个不同对象的沟通集中到中介者对象中
备忘录 Memento
- 示例:游戏自动存储
- 作用:需要将对象返回到之前的状态
- 目标
- 储存系统关键对象的重要状态
- 维护关键对象的封装
- 理解:
- 创建3个类:
- Originator 创建Memento对象并进行存储状态/从Caretaker获取Memento对象并进行恢复
- Memento 包含了要被恢复的对象的状态
- Caretaker 维护Memento列表,记录状态变化
- 创建3个类:
原型 Prototype
- 示例:自动刷新小怪兽
- 应用场景:
- 创建给定类的实例的过程很昂贵或很复杂
- 需要一个类的大量对象
- 理解: implements java.lang.Cloneable & rewrite clone()
- 创建原型接口,提供接口中的clone方法
- 对象实现原型接口及对应的clone方法
- 当创建对象代价较大时,则使用clone方法进行对象创建
访问者 Visitor
- 示例:菜单中的营养成分获取
- 应用场景:需要为一个对象的组合增加新的能力,并且封装并不重要时
- 理解:在被访问类中加入一个对外提供接待的访问者接口