设计模式之禅
2019-06-05 08:49:02 0 举报
AI智能生成
设计模式之禅
作者其他创作
大纲/内容
23种设计模式
单例模式
定义
确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例
优点
减少内存开支
减少系统的性能开销
可以避免对资源的多重占用
可以在系统设置全局的访问点,优化和共享资源访问
缺点
单例模式一般没有接口,扩展困难
不利于测试
单例与单一职责原则有冲突
使用场景
要求生成唯一序列号的环境
在整个项目中需要一个共享访问点或共享数据
创建一个对象需要消耗的资源过多
需要定义大量的静态常量和静态方法(如工具类)的环境
注意事项
在高并发情况下,注意线程同步问题
工厂方法模式
定义
一个用于创建对象的接口,让子类决定实例化哪一个类,工厂方法使一个类的实例化延迟到其子类
优点
良好的封装性,代码结构清晰
扩展性好,在增加产品类的情况下,只要适当地修改具体的工厂类或扩展一个工厂类,就可以拥抱变化
屏蔽产品类,产品类的实现如何变化,调用者不需要关心,它只需要关心产品的接口,只要接口保持不变,系统中的上层模块就不要发生变化
解耦框架,高层模块只需要知道产品的抽象类,其他的实现类都不用关心
使用场景
工厂方法模块是new一个对象的替代品,所以在所有需要生成对象的地方都可以使用,但是需要慎重地考虑是否要增加一个工厂类进行管理
需要灵活的、可扩展的框架时,可以考虑采用工厂方法模式
可以使用在测试驱动开发的框架下
扩展
缩小为简单工厂模式
升级为多个工厂类
替代单例模式
延迟初始化
抽象工厂模式
定义
为创建一组相关或相互依赖的对象提供一个接口,而且无需指定它们的具体类
优点
封装性
每个产品的实现类不是高层模块要关心的,只需关心接口或抽象即可
产品族内的约束为非公开状态
缺点
产品族扩展非常困难
使用场景
一个对象族(或是一组没有人任何关系的对象)都有相同的约束,则可以使用抽象工厂模式
模板方法模式
定义
定义一个操作中的算法的框架,而将一些步骤延时到子类中,使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤
基本方法
由子类实现的方法,并且在模板方法被调用
模板方法
可以有一个或几个,一般是一个具体方法,也就是一个框架,实现对基本方法的调度,完成固定的逻辑
优点
封装不变部分,扩展可变部分
提取公共部分代码,便于维护
行为由父类控制,子类实现
缺点
子类执行的结果会影响父类的结果,在复杂的项目中,会带来代码阅读的难度
建造者模式
定义
将一个复杂对象的构造与它的表示分离,使得同样的构建过程可以创建不同的表示
角色
Product产品类
实现了模板方法模式,也就是有模板方法和基本方法
Builder抽象建造者
规范产品的组建,一般是由子类实现
ConcreteBuilder具体建造者
实现Builder抽象建造者定义的所有方法,并且返回一个组建好的对象
Director导演类
负责安排已有模块的顺序,然后告诉Builder开始建造
优点
封装性
使用建造者模式可以使客户端不必知道产品内部组成的细节
建造者独立,容易扩展
便于控制细节风险
由于具体的建造者是独立的,因此可以对建造过程逐步细化,而不对其他的模块产生任何影响
使用场景
相同的方法,不同的执行顺序,产生不同的事件结果时,可以采用建造者模式
多个部件或零件,都可以装配到一个对象中,但是产生的运行结果又不相同时,则可以使用该模式
产品类非常复杂,或者产品类中的调用顺序不同产生了不同的效能
在对象创建过程中会使用到系统中的一些其他对象,这些对象在产品对象的创建过程中不易得到时,也可以采用建造者模式封装该对象的创建过程
注意事项
建造者模式关注的是零件类型和装配工艺(顺序),这是它与工厂方法模式最大不同的地方,虽然同为创建类模式,但是注重点不同
代理模式
定义
为其他对象提供一种代理以控制对这个对象的访问
角色
Subject抽象主题角色
可以是抽象类也可以是接口,是一个最普通的业务类型定义,无特殊要求
RealSubject具体主题角色
业务逻辑的具体执行者
Proxy代理主题角色
负责对真实角色的应用,把所有抽象主题类定义的方法限制委托给真实主题角色实现,并且在真实主题角色处理完毕前后做预处理和善后处理工作
优点
职责清晰
真实的角色是实现实际的业务逻辑,不用关心其他非本职责的事物,通过后期的代理完成一件事务,附带的结果就是编程简洁清晰
高扩展性
智能化
扩展
普通代理
客户端只能访问代理角色,而不能访问真实角色
强制代理
必须通过真实角色找到代理角色
虚拟代理
在需要的时候才初始化对象,可以避免被代理对象较多而引起的初始化缓慢的问题
动态代理
在实现阶段不用关心代理谁,而在运行阶段才指定代理哪一个对象,面向横切面编程(AOP),其核心就是采用了动态代理机制
原型模式
定义
用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象
优点
性能优良
原型模式是在内存二进制流的拷贝,要比直接new一个对象性能好很多,特别是要在一个循环体内产生大量的对象时,原型模式可以更好地体现其优点
逃避构造函数的约束
直接在内存中拷贝,构造函数是不会执行的,实际使用时应考虑这一点
使用场景
资源优化场景
类初始化需要消化非常多的资源
性能和安全要求的场景
通过new产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式
一个对象多个修改者的场景
一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用
注意事项
构造函数不会被执行
浅拷贝和深拷贝
clone和final
要使用clone方法,在类的成员变量上就不要增加final关键字
中介者模式
定义
用一个中介对象封装一系列的对象交互,中介者使各对象不需要显示地相互作用,从而使其耦合松散,而且可以独立地改变它们之间的交互
角色
Mediator抽象中介者角色
定义统一的接口,用于各同事角色之间的通信
Concrete Mediator具体中介者角色
具体中介者角色通过协调各同事角色实现协助行为,因此它必须依赖于各个同事角色
Colleague同事角色
每个同事角色都知道中介者角色,而且与其他的同事角色通信的时候,一定要通过中介者角色协作
每个同事类的行为分为两种
自发行为
与其他的同事类或中介者没有任何的依赖
依赖行为
必须依赖中介者才能完成的行为
优点
减少类间的依赖
把原有的一对多的依赖变成了一对一的依赖
缺点
中介者会膨胀得很大,而且逻辑复杂,原本N个对象直接的相互依赖关系转换为中介者和同事类的依赖关系,同事类越多,中介者的逻辑就越复杂
使用场景
中介者模式适用于多个对象之间紧密耦合的情况
紧密耦合的标准
在类图中出现了蜘蛛网状结构,使用中介者模式有利于把蜘蛛网梳理为星型结构
实际使用
机场调度中心
MVC框架
媒体网关
中介服务
MVC框架
最佳实际
如果两个对象不能提炼出共性,那就不要刻意去追求两者的抽象,抽象只要定义出模式需要的角色即可
一个中介者抽象类一般只要一个实现者,除非中介者逻辑非常复杂,代码量非常大,这时才会出现多个中介者的情况。所以,对于中介者来说,抽象已经没有太多的必要
命令模式
责任链模式
装饰模式
策略模式
适配器模式
迭代器模式
组合模式
观察者模式
门面模式
备忘录模式
访问者模式
状态模式
解析器模式
享元模式
桥梁模式
原则
单一指责原则(SRP)
英文
Single Responsibility Principle
定义
应该有且仅有一个原因一起类的变更
好处
类的复杂性减低,实现什么职责都有清晰明确的定义
可读性提高
可维护性提高
变更引起的风险 降低,变更是必不可少的,如果接口的单一职责做的好,一个接口修改只对相应的实现类有影响,对其他接口无影响,这对系统的扩展性、维护性都有非常大的帮助
最佳实践
接口一定要做到单一职责,类的设计尽量做到只有一个原因引起变化
里氏替换原则(LSP)
英文
Liskov Substitution Principle
定义
如果对每一个类型为S的对象o1,都有类型为T的对象o2,使得以T定义的所有程序P在所有的对象o1都代换成o2时,程序P的行为没有发生变化,那么类型S是类型T的子类型
只要父类能出现的地方子类就可以出现,而且替换为子类也不会产生任何错误或异常,使用者可能更不就不需要知道是父类还是子类。但是,反过来就不行了,有子类出现的地方,父类未必就能适应
继承规范
子类必须完全实现父类的方法
子类可以有自己的个性
覆盖或实现父类的方法时输入参数可以被放大
覆写或实现父类的方法时输出结果可以被缩小
好处
增强程序的健壮性,版本升级时也可以保持非常好的兼容性。即使增加子类,原有的子类也可以继续运行。
最佳实践
尽量避免子类的“个性”,一旦子类有“个性”,这个子类和父类之间的关系就难调和了,把子类当做父类使用,子类的“个性”被抹杀;把子类单独作为一个业务来使用,则会让代码间的耦合关系变得扑朔迷离
依赖倒置原则(DIP)
英文
Dependence Inversion Principle
定义
高层模块不应该依赖低层模块,两者都应该依赖其抽象
抽象不应该依赖细节
细节应该依赖抽象
面向接口编程(OOD)
最佳实践
每个类尽量都有接口或抽象类,或者抽象类和接口两者都具备
变量的表面类型尽量时接口或者时抽象类
任何类都不应该从具体类派生
尽量不要覆写基类的方法
结合里氏替换原则使用,接口负责定义public属性和方法,并且声明与其他对象的依赖关系,抽象类负责公共构造部分的实现,实现类准确的实现业务逻辑,同时在适当的时候对父类进行细化
接口隔离原则
英文
定义
客户端不应该依赖它不需要的接口
类间的依赖关系应该建立在最小的接口上
保证接口的纯洁性
接口要尽量小
接口要高内聚
定制服务(只提供访问者需要的方法)
接口设计是有限度的
最佳实践
一个接口只服务与一个子模块或业务逻辑
通过业务逻辑压缩接口中的public方法,接口时常去回顾,尽量让接口达到“满身筋骨肉”,而不是“肥嘟嘟”的一大堆方法
已经被污染了的接口,尽量去修改,若变更的风险较大,则采用适配器模式进行转化处理
了解环境,拒绝盲从。每个项目或产品都有特定的环境因素,别看到大师是这样做的你就照抄。环境不同,接口拆分的标准就不同
迪米特法则(LoD)
英文
Law of Demeter
定义
一个对象应该对其他对象有最少的了解
要求
只和朋友交流
朋友间也是有距离的
是自己的就是自己的
如果一个方法放在本类中,即不增加类间关系,也对本类不产生负面影响,就放置在本来中
谨慎使用Serializable
最佳实践
类间解耦,弱耦合,只有若耦合以后,类的复用率才可以提高
在实际应用中,如果一个类跳转两次以上才能访问到另一个类,就需要想办法进行重构了,因为一个系统的成功不仅仅是一个标准或原则就能决定的,有非常多的外在因素决定,跳转次数越多,系统越复杂,维护就越困难,所以只要跳转不超过两次都是可以忍受的,这需要具体问题具体分析
解耦是有限度的
开闭原则
定义
一个软件实体如类、模块和函数应该对扩展开发,对修改关闭
软件实体应该通过扩展来实现变化,而不是通过修改已有的代码来实现变化
最佳实践
抽象约束
元数据(metadata)控制模块行为
配置参数
制定项目章程
封装变化
将相同的变化封装到一个接口或抽象类中
将不同的变化封装到不同的接口或抽象类中,不应该有两个不同的变化出现在同一个接口或抽象类中
找出预计有变化或不稳定的点
0 条评论
下一页