设计模式读书笔记
设计模式这是第三遍看了,每工作两年回头看都会有新的一些感受,最近时间比较宽裕把基础的知识复习复习巩固巩固。
基础知识
读设计模式之前要有一些UML的基础知识
虚线箭头指向依赖;
实线箭头指向关联;
虚线三角指向接口;
实线三角指向父类;
空心菱形能分离而独立存在,是聚合;
实心菱形精密关联不可分,是组合;
面向对象设计原则
单一职责原则(SRP)、开放封闭原则(OCP)、里氏代替原则(LSP)、依赖倒置原则(DIP)、接口隔离原则(ISP)、合成复用原则(CRP)和迪米特原则(LoD)
单一职责原则
就一个类而言,应该仅有一个引起它变化的原因。
开放封闭原则
就是说软件实体(类,方法等等)应该可以扩展(扩展可以理解为增加),但是不能在原来的方法或者类上修改,也可以这样说,对增加代码开放,对修改代码关闭。
里氏代替原则
子类型必须能够替换掉它们的父类型。更直白的说,LSP是实现面向接口编程的基础。
依赖倒置原则
抽象不应该依赖细节,细节应该依赖于抽象。简单说就是,我们要针对接口编程,而不要针对实现编程。
高层模块不应该依赖低层模块,两个都应该依赖抽象,因为抽象是稳定的。抽象不应该依赖具体(细节),具体(细节)应该依赖抽象。
接口隔离原则
使用多个专门的接口比使用单一的总接口要好。也就是说不要让一个单一的接口承担过多的职责,而应把每个职责分离到多个专门的接口中,进行接口分离。过于臃肿的接口是对接口的一种污染。
合成复用原则
尽量使用合成/聚合,尽量不要使用类继承。
聚合表示一种弱的‘拥有’关系,体现的是A对象可以包含B对象,但B对象不是A对象的一部分;合成则是一种强的‘拥有’关系,体现了严格的部分和整体的关系。
迪米特原则
又叫最少知识原则(Least Knowledge Principle,LKP),指的是一个对象应当对其他对象有尽可能少的了解。也就是说,一个模块或对象应尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立,这样当一个模块修改时,影响的模块就会越少,扩展起来更加容易。
设计模式
创建型
- 单例模式
只保证有一个类,lock防止多线程并发。
- 工厂方法
一种工厂生产一种产品,工厂类和产品类是一一对应的,他们是平行的等级结构,强调的是“单个对象”的变化。
- 抽象工厂方法
该模式关注的是多批多系列相互依赖的产品的变化
- 建造模式
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
建造小人的例子:
Director控制建造小人的步骤,Builder定义具体各个部件的建造方法返回具体产品
- 原型模式
.net的ICloneable实现了原型模式,注意深复制浅复制。
结构型
- 适配器模式
对象适配器
类适配器
将一个类的接口转换成客户希望的另一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
适配器实现分为:类适配器和对象适配器两种。
Target对应正常需求,Adaptee对应特殊需求,Adapter继承Target重写Request调用Adaptee的特殊方法,如果需要适配初始化Target为适配器。
.net中各种数据库并没有提供DataSet接口,使用DbDataAdapter可以将任何个数据库访问/存取适配到一个DataSet对象上,DbDataAdapter在数据库和DataSet之间做了很好的适配。
- 桥接模式
将抽象部分与实现部分分离,使它们都可以独立地变化。
手机品牌和手机软件的例子,手机就是被提炼的抽象,软件就是具体的实现,手机有很多品牌,软件有很多种类,具体什么品牌上运行什么软件在程序运行时候可以自定义选择。
- 装饰模式
有个接口具有打扮功能,人继承这个接口,通过化妆进行打扮,服饰也继承这个接口对人进行装扮,所有具体饰品继承服饰类,装扮接口先运行基类的装饰功能,这样装饰想过就层层遍历进行装饰。
namespace 装饰模式的实现
{
public interface 打扮接口
{
void 打扮();
}
public class 人 : 打扮接口
{
public void 打扮()
{
Console.WriteLine("摸摸口红化化妆");
}
}
public abstract class 服饰 : 打扮接口
{
protected 打扮接口 res;
protected 服饰(打扮接口 res)
{
this.res = res;
}
public virtual void 打扮()
{
if (this.res != null)
{
this.res.打扮();
}
}
}
public sealed class 衣服 : 服饰
{
public 衣服(打扮接口 res) : base(res) { }
public override void 打扮()
{
base.打扮();
Console.WriteLine("穿上漂亮的衣服");
}
}
public sealed class 裙子 : 服饰
{
public 裙子(打扮接口 res) : base(res) { }
public override void 打扮()
{
base.打扮();
Console.WriteLine("穿上漂亮的裙子");
}
}
public class Program
{
static void Main()
{
人 person = new 人();
衣服 yf = new 衣服(person);
裙子 qz = new 裙子(yf);
qz.打扮();
//显示结果:
//摸摸口红化化妆
//穿上漂亮的衣服
//穿上漂亮的裙子
Console.Read();
}
}
}
- 组合模式
该模式着重解决统一接口的问题,将“一对多”的关系转化为“一对一”的关系,让使用叶子节点和树干节点保持接口一致。
Winform窗体就是典型的组合模式,窗体等可以嵌套控件,文本等就是leaf节点,Groupbox和Panel等就是Composite。
- 外观模式
该模式注重简化接口,简化组件系统与外部客户程序的依赖关系,是Lod(迪米特原则,也叫:最少知识原则)原则的最好的体现。
- 享元模式
运用共享技术有效地支持大量细粒度的对象。
缓存就是采用的享元模式。
- 代理模式
为其他对象提供一种代理以控制对这个对象的访问。代理和真实调用者都继承同一方法,代理在这个方法中调用真实调用者的方法同时还能加一些特殊的功能。
行为型
- 模板方法
定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类不可以改变一个算法的结构即可重定义该算法的某些特定步骤。
- 命令模式
将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。
例子:烧烤点菜,烧烤师是Receiver,菜单是ConCreteCommand,服务员是Invorker
- 迭代器模式
提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露该对象内部表示。
.net中该命名空间就是:System.Collections,在该命名空间里面早已有了迭代器模式的实现。对于聚集接口和迭代器接口已经存在了,其中IEnumerator扮演的就是迭代器的角色,IEnumerable则扮演的就是抽象聚集的角色,只有一个GetEnumerator()方法,如果集合对象需要具备跌代遍历的功能,就必须实现该接口。
- 观察者模式
该模式注重的是变化通知,变化通知指目标对象发生变化,依赖的对象就能获得通知并进行相应操作,这是一种“一对多”的关系。
例如信息订阅,订阅者订阅后,信息发布后遍历通知订阅者。
- 中介者模式
该模式注重封装对象间的交互,通过封装一系列对象之间的复杂交互,使他们不需要显式相互引用,实现解耦。
例如联合国,所有国家都是通过联合国相互通讯。国家初始化构造函数要设置联合国,联合国也要设置国家属性才能相互通讯。
- 状态模式
当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类,
Context中的Request操作执行的是state中的Handle操作,ConcreteState的Handle操作会将Context中的state改变,这样Context不停进行Request操作,State一直改变而导致不同的操作。
- 策略模式
该模式注重封装算法,这里面没有算法骨架,一种算法就是一种解决方案,一种方法策略。支持算法的变化,通过封装一系列算法,可以做到算法的替换来满足客户的需求。区别简单工厂。
- 责任链模式
该模式注重封装对象责任,支持责任的变化,通过动态构建职责链,实现业务处理。在现实生活中,请假流程,采购流程等都是职责链模式很好的例子。
每个Handle设置successor继承者,在HandleReuqest操作中如果满足条件自己操作,如果不满足successor操作,形成一条责任链。
- 访问者模式
访问者适用于数据结构相对稳定的系统,男人女人对应各种状态的例子。
- 备忘录模式
在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。
游戏进度保存,三个角色,人物角色,当前进度,进度管理器。人物角色的当前进度赋值给当前进度,进度管理器保存当前进度,当人物角色状态发生变化,再从进度管理器中取。
- 解释器模式
相当于翻译软件,编译器等该算法比较复杂,大致了解就可以。
参考资料:
UML类图与类的关系详解
C#设计模式之总结篇
【大话设计模式】