设计原则
- 把可能变化之处独立出来,以免影响固定代码。
- 针对接口编程,而不是针对实现编程
- 多用组合,少用继承
- 设计交互对象之间的松耦合
- 扩展-关闭原则:类应该对扩展开发,对修改关闭。
- 依赖抽象,不要依赖具体类。
- 最少知识原则:只和你的密友谈话。
- 好莱坞原则:高层组件对待低层组件的方式:别调用我们,我们会调用你。
避免高层和低层出现明显的环装依赖 - 一个类应该只有一个引起变化的原因。高内聚,低耦合。(单一职责)
备注
1 | //推荐 |
如果某对象是调用其他方法的返回结果,不要调用它的方法。意味着,不清楚他的方法细节。并不认识此对象,也就不依赖它。
针对接口(超类型)编程
针对实现:
1 | Dog d=new Dog(); |
针对接口:
1 | Animal a=new Dog(); |
运行时:
1 | Animal a=getAnimal(); |
策略模式
定义
定义了算法族,分别封装起来,让它们之间可以互相替换。此模式让算法的变化独立于使用算法的客户。
类图
观察者模式
定义
定义了对象间的一对多依赖(订阅),这样依赖,当一个对象改变时,它的所有依赖者都会受到通知并更新。Subject主题,Observer观察者。
类图
代码示例
另可以用多播委托实现此过程。
1 | public class DeleteAction : IDisposable |
装饰者模式
定义
动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案。
类图
代码示例
1 | public abstract class Phone |
角色
抽象构件(Phone)角色:给出一个抽象接口,以规范准备接受附加责任的对象。
具体构件(AppPhone)角色:定义一个将要接收附加责任的类。
装饰(Dicorator)角色:持有一个构件(Component)对象的实例,并定义一个与抽象构件接口一致的接口。
具体装饰(Sticker和Accessories)角色:负责给构件对象 ”贴上“附加的责任。
理解
优点:
装饰者模式和继承的目的都是扩展对象的功能,但装饰者模式比继承更灵活通过使用不同的具体装饰类以及这些类的排列组合,设计师可以创造出很多不同行为的组合。装饰者模式有很好地可扩展性。
缺点:
装饰者模式会导致设计中出现许多小对象,如果过度使用,会让程序变的更复杂。并且更多的对象会是的差错变得困难,特别是这些对象看上去都很像。
适配器模式
定义
将一个类的接口,转换成客户(使用者)期望的另一个接口。适配器让原本不兼容的类可以合作无间。
类图
代码示例
1 | public interface IDuck |
理解
系统A,原本某一块功能(如客户出入金)需要从一个提供商甲切换到提供商乙,不改变系统代码的情况下。可以创建适配器,封装乙的接口实现切换动作,让系统仍然像使用甲的接口那样,正常运作。过程不需修改代码,只需扩展适配器。处理遗留问题。
装饰者和适配器的异同
适用条件的差别:
装饰模式一般在下列情况使用:需要扩展一个类的功能或者给你个类增加附加责任;需要动态的给一个对象增加功能,这些功能可以再动态的撤销;需要增加有一些基本功能的排列组合而产生非常大量的功能,从而使得继承关系变得不现实。
适配器模式一般使用的情况包括:系统需要使用现有的类,但此类已经不符合系统的需要;
想要建立一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的的类一起工作。适配器模式在系统升级的时候使用的频率很高,对旧系统的一些功能方法在新系统中引用。
单例模式
定义
确保一个类只有一个实例,并提供一个全局访问点。
代码示例
线程安全
1 | public class Singleton |
命令模式
定义
将“请求”封装成对象,以便使用不同的请求、队列或日志来参数化其他对象。命令模式也支持可撤销的操作。
类图
代码示例
1 | public class Receiver |
理解
将请求者和执行者(接受者)解耦:
如果执行者种类繁多,且不属于同一个体系,也没有统一执行接口。请求者请求命令,需要知道所有执行者的外露接口,耦合很高。
命令模式,加入中间层:调用者,让请求者只认识调用者,向调用者发出指定执行者且接口固定的命令对象,调用者只执行指定命令对象的固定接口,命令对象包含执行者的引用和一组方法,知道如何完成操作。这种封装命令对象的模式,为命令模式。如待完成列表,可以在任何时间被执行。
外观模式
定义
提供了一个统一的接口,用来访问子系统中的一群接口。外观定义了一个高层接口,让子系统更容易适用。
类图
代理模式
定义
为另一个对象提供一个替身或占位符以控制对这个对象的访问。
类图
代理分类
- 远程代理:为一个位于不同的地址空间的对象提供一个局域代表对象。这个不同的地址空间可以是在本机器中,也可是在另一台机器中。这样可以隐藏一个对象在不同地址空间的事实。
例如:WebService,WCF - 虚拟代理: 根据需要创建一个资源消耗较大的对象,通过它来存放实例化需要长时间的真实对象,使得此对象只在需要时才会被真正创建
- 保护代理:用来控制真实对象的访问时的权限。如果需要,可以给不同的用户提供不同级别的使用权限
- 智能代理:指当调用真实对象时,代理处理另外一些事。提供一些额外的操作,比如将对此对象调用的次数记录下来等
- 同步(Synchronization)代理:多线程情况下,为主题提供安全的访问
- 复杂隐藏(Complexity Hiding)代理:隐藏一个类的复杂集合的复杂度,有时候称为外观代理(Facade Proxy)。此代理控制访问,外观模式只提供另一组接口
- 写入时复制(Copy-on-Write)代理:虚拟代理的变种。把复制(克隆)延迟到只有在客户端需要时,才真正采取行动
- 缓存(Cache)代理:为开销大的运算结果提供临时的存储空间,以便多个客户端可以共享这些结果,以减少计算或网络延迟
- 防火墙(Firewall)代理:控制网络资源的访问,不让恶意用户接近
装饰者,外观,代理,适配器四种模式的比较
工厂方法模式
定义
定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类。
类图
抽象工厂模式
定义
提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指明具体类。
类图
模板方法模式
定义
在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。
类图
代码示例
1 | public abstract class Beverage |
模板方法模式的变种
不使用继承,而使用组合。
java中Arrays.sort(object[] arr)中的算法,进行两个对象之间的比较。
1 | Comparable obj... //接口 |
算法待实现部分,不再交由子类负责实现(数组不可继承)。而是传入一个实现了Comparable接口的元素组成的数组,由此数组的元素负责。
模板方法模式和工厂方法模式,区别
模板方法,指的是实现算法的方法,包含基类实现部分和待子类实现部分(抽象方法)。
工厂方法,指的是可由子类继承实现的抽象方法,不同子类决定不同的功能。会由基类调用。
迭代器模式
定义
提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示。
类图
代码示例
1 | public class Item |
组合模式
定义
允许你将对象组合成树形结构来表现“整体/部分”层次结构。组合能让客户以一致的方式处理个别对象以及对象组合。
安全式和透明式组合模式:
透明式:以单一责任设计原则换取透明性。减少客户代码,失去了一些安全性。可能会抛出异常。
安全式:将责任区分开放在不同的接口中,符合单一责任原则。比较安全,但失去了透明性。客户代码必须判断类型,处理不同类型的节点。
类图
透明式的组合模式
1 | public class Graphics |
安全式的组合模式
1 | public abstract class Graphics |
状态模式
定义
允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类。
类图
代码示例
Machine:
1 | public class Machine |
状态类:
1 | internal abstract class State |
测试代码:
1 | class Program |