设计模式总结
设计模式总结:
六大设计原则:
一、单一职责原则
就一个类而言,应该仅有一个引起它变化的原因。做编程的时候,如果讲每一个类加上各种各样的功能就意味着,无论任何需求要来,你都需要更改这个类,这样会让维护非常麻烦,复用不可能,也缺乏灵活性。如果一个类承担的职责过多,就等于把这些职责耦合起来,一个职责变化可能会削弱或者抑制这个类完成其他职责的能力。这种耦合会导致脆弱的设计,当变化发生时,设计会遭到很多意想不到的破坏。
二、里氏代换原则
里氏代换原则是由"Barbara Liskov"提出的。如果调用的是父类的话,那么换成子类也完全可以运行。Java编译程序会检查程序是否符合里氏代换原则。还记得java继承的一个原则吗?子类override方法的访问权限不能小于父类对应方法的访问权限。可以说里氏代换原则是继承复用的一个基础。
三、依赖倒置原则
这个原则是开闭原则的基础,具体内容:针对接口编程,依赖于抽象而不依赖于具体。
四、接口隔离原则
使用多个隔离的接口,比使用单个接口要好。它还有另外一个意思是:降低类之间的耦合度。由此可见,其实设计模式就是从大型软件架构出发、便于升级和维护的软件设计思想,它强调降低依赖,降低耦合。
五、迪米特法则
一个实体应当尽量少地与其他实体之间发生相互作用,使得系统功能模块相对独立。
六、开闭原则
对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。简言之,是为了使程序的扩展性好,易于维护和升级。
创建型模式
一、Singleton单件 模式分类
从目的来看:
1.创建型(Creational)模式:负责对象创建。
2.结构型(Structural)模式:处理类与对象间的组合。
3.行为型(Behavioral)模式:类与对象交互中的职责分配。
从范围来看:
1.类模式处理类与子类的静态关系。
2.对象模式处理对象间的动态关系。
动机
在软件系统中,经常有这样一些特殊的类,必须保证它们在系统中只存在一个实例,才能确保它们的逻辑正确性、以及良好的效率。
意图
保证一个类仅有一个实例,并提供一个该实例的全局访问点。——《设计模式》
单线程Singleton模式的几个要点:
(1)Singleton模式中的实例构造器可以设置为protected以允 许子类派生。
(2)Singleton模式一般不要支持ICloneable接口,因为这可能 会导致多个对象实例,与Singleton模式的初衷违背。
(3) Singleton模式一般不要支持序列化,因为这也有可能导致 多个对象实例,同样与Singleton模式的初衷违背。
(4) Singletom模式只考虑到了对象创建的管理,没有考虑对 象销毁的管理。就支持垃圾回收的平台和对象的开销来
讲,我们一般没有必要对其销毁进行特殊的管理。
(5)不能应对多线程环境:在多线程环境下,使用Singleton模 式仍然有可能得到Singleton类的多个实例对象。
Singleton模式扩展:
(1)将一个实例扩展到n个实例,例如对象池的实现。
(2) 将new 构造器的调用转移到其他类中,例如多个类协同工作环境中,某个局部环境只需要拥有某个类的一个实例。
(3)理解和扩展Singleton模式的核心是“如何控制用户使用new对一个类的实例构造器的任意调用”。
二、Abstract Factory 抽象工厂
1、new的问题
常规的对象创建方法:
// 创建一个Road 对象
Road road=new Road();
new的问题:
– 实现依赖,不能应对“具体实例化类型”的变化。
解决思路:
– 封装变化点 —— 哪里变化,封装哪里
– 潜台词:如果没有变化,当然不需要额外的封装!
2、工厂模式的缘起
• 变化点在“对象创建”,因此就封装“对象创建”
• 面向接口编程——依赖接口,而非依赖实现
• 最简单的解决方法:
class RoadFactory {
public static Road CreateRoad()
{
return new Road();
}
}
// 创建一个Road 对象
Road road=
roadFactory.CreateRoad();
3、Abstract Factory模式的几个要点
• 如果没有应对“多系列对象构建”的需求变化,则没有必要使用Abstract Factory模式,这时候使用简单的静态工厂 完全可以。
• “系列对象”指的是这些对象之间有相互依赖、或作用的关系,例如游戏开发场景中的“道路”与“房屋”的依赖,“道路”与“地道”的依赖。
• Abstract Factory模式主要在于应对“新系列”的需求变动。其缺点在于难以应对“新对象”的需求变动。
• Abstract Factory模式经常和Factory Method模式共同组合 来应对“对象创建”的需求变化。
三、Builder 生成器(创建型模式)
将一个复杂对象的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。 ——《设计模式》
Builder 模式的几个要点:
• Builder 模式主要用于“分步骤构建一个复杂的对象”。在这其中“分步骤”是一个稳定的算法,而复杂对象的各个部分则经常变化。
• 变化点在哪里,封装哪里—— Builder模式主要在于应对“复杂对象各个部分”的频繁需求变动。其缺点在于难以应对“分步骤构建算法”的需求变动。
• Abstract Factory模式解决“系列对象”的需求变 化,Builder模式解决“对象部分”的需求变化。Builder模式通常和Composite模式组合使用。
四、Factory Method 工厂方法
1、从耦合关系谈起,耦合关系直接决定着软件面对变化时的行为。
– 模块与模块之间的紧耦合使得软件面对变化时,相关的模块都要随之更改。
– 模块与模块之间的松耦合使得软件面对变化时,一些模块更容易被替换或者更改,但其他模块保持不变。
2、意图(Intent)
定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method使得一个类的实例化延迟到子类。 ——《设计模式》
3、Factory Method模式的几个要点:
• Factory Method模式主要用于隔离类对象的使用者和具体类型之间的耦合关系。面对一个经常变化的具体类型,紧耦合关系会导致软件的脆弱。
• Factory Method模式通过面向对象的手法,将所要创建的具体对象工作延迟到子类,从而实现一种扩展(而非更改)的策略,较好地解决了这种紧耦合关系。
• Factory Method模式解决“单个对象”的需求变化,Abstract Factory 模式解决“系列对象”的需求变化,Builder模式解决“对象部分”的需求变化。
五、Prototype 原型
1、依赖关系的倒置
– 抽象
抽象不应该依赖于实现细节,实现细节应该依赖于抽象。
2、意图(Intent)
使用原型实例指定创建对象的种类,然后通过拷贝这些原型来创建新的对象。 ——《设计模式》
3、Prototype模式的几个要点:
• Prototype模式同样用于隔离类对象的使用者和具 体类型(易变类)之间的耦合关系,它同样要求 这些“易变类”拥有“稳定的接口”。
• Prototype模式对于“如何创建易变类的实体对象” 采用“原型克隆”的方法来做,它使得我们可以非常灵活地动态创建“拥有某些稳定接口”的新对象——所需工作仅仅是注册一个新类的对象(即原型),然后在任何需要的地方不断地Clone。
• Prototype模式中的Clone方法可以利用.NET中的Object类的MemberwiseClone()方法或者序列化 来实现深拷贝。
4、有关创建性模式的讨论
• Singleton模式解决的是实体对象个数的问题。除了Singleton之外,其他创建型模式解决的都是new所带来的耦合关系。
• Factory Method, Abstract Factory, Builder都需要 一个额外的工厂类来负责实例化“易变对象”,而Prototype则是通过原型(一个特殊的工厂类)来 克隆“易变对象”。
• 如果遇到“易变类”,起初的设计通常从FactoryMethod开始,当遇到更多的复杂变化时,再考虑重构为其他三种工厂模式
面向对象:
1、设计模式与面向对象
• 面向对象设计模式解决的是“类与相互通信的对象之间的组织关系,包括它们的角色、职责、协作方式几个方面。
• 面向对象设计模式是“好的面向对象设计”,所谓“好的面向对象设计”是那些可以满足“应对变化,提高复用”的设计 。
• 面向对象设计模式描述的是软件设计,因此它是独立于编程语言的,但是面向对象设计模式的最终实现仍然要使用面向对象编程语言来表达,本课程基于C#语言,但实际上它适用于支持.NET框架的所有.NET语言,如Visual Basic.NET、C++/CLI等。
• 面向对象设计模式不像算法技巧,可以照搬照用,它是建立在对“面向对象”纯熟、深入的理解的基础上的经验性认识。掌握面向对象设计模式的前提是首先掌握“面向对象”!
2、从编程语言直观了解面向对象
• 各种面向对象编程语言相互有别,但都能看到它们对面向对象三大机制的支持,即:“封装、继承、多态”
– 封装,隐藏内部实现
– 继承,复用现有代码
– 多态,改写对象行为
• 使用面向对象编程语言(如C#),可以推动程序员以面向对象的思维来思考软件设计结构,从而强化面向对象的编程范式。
• C#是一门支持面向对象编程的优秀语言,包括:各种级别的封装支持;单实现继承+多接口实现;抽象方法与虚方法重写。
但OOPL并非面向对象的全部
• 通过面向对象编程语言(OOPL)认识到的面向对象,并不是面向对象的全部,甚至只是浅陋的面向对象。
• OOPL的三大机制 “封装、继承、多态” 可以表达面向对象的所有概念,但这三大机制本身并没有刻画出面向对象的核心精神。换言之, 既可以用这三大机制做出 “好的面向对象设计”,也可以用这三大机制做出 “差的面向对象设计”。不是使用了面向对象的语言(例如C#),就实现了面向对象的设计与开发!因此我们不能依赖编程语言的面向对象机制,来掌握面向对象。
3、重新认识面向对象
对象是什么?
– 从概念层面讲,对象是某种拥有责任的抽象。
– 从规格层面讲,对象是一系列可以被其他对象使用的公共接口。
– 从语言实现层面来看,对象封装了代码和数据。
4、从设计原则到设计模式
• 针对接口编程,而不是针对实现编程– 客户无需知道所使用对象的特定类型,只需要知道对象拥有客户所期望的接口。
• 优先使用对象组合,而不是类继承– 类继承通常为“白箱复用”,对象组合通常为“黑箱复用”。继承在某种程度上破坏了封装性,子类父类耦合度高;而对象组合则只要求被组合的对象具有良好定义的接口,耦合度低。
• 封装变化点– 使用封装来创建对象之间的分界层,让设计者可以在分界层的一侧进行修改,而不会对另一侧产生不良的影响,从而实现层次间的松耦合。
• 使用重构得到模式——设计模式的应用不宜先入为主,一上来就使用设计模式是对设计模式的最大误用。没有一步到位的设计模式。敏捷 软件开发实践提的“Refactoring to Patterns”是目前普遍公认的最好 的使用设计模式的方法。
5、几条更具体的设计原则
• 单一职责原则(SRP):– 一个类应该仅有一个引起它变化的原因。
• 开放封闭原则(OCP):– 类模块应该是可扩展的,但是不可修改(对扩展开放,对更改封闭)
• Liskov 替换原则(LSP):– 子类必须能够替换它们的基类
• 依赖倒置原则(DIP):– 高层模块不应该依赖于低层模块,二者都应该依赖于抽象。– 抽象不应该依赖于实现细节,实现细节应该依赖于抽象。
• 接口隔离原则(ISP):– 不应该强迫客户程序依赖于它们不用的方法。
6、总结
设计模式描述了软件设计过程中某一类常见问题的一般性的解决方案。面向对象设计模式描述了面向对象设计过程中、特定场景下、类与相互通信的对象之间常见的组织关系。
• 深刻理解面向对象是学好设计模式的基础,掌握一定的面向对象设计原则才能把握面向对象设计模式的精髓,从而实现灵活运用设计模式。
• 三大基本面向对象设计原则
– 针对接口编程,而不是针对实现编程
– 优先使用对象组合,而不是类继承
– 封装变化点
• 使用重构得到模式。敏捷软件开发实践提倡的“Refactoring toPatterns”是目前普遍公认的最好的使用设计模式的方法。
结构型模式
Adapter模式注重转换接口,将不吻合的接口适配对接
Bridge模式注重分离接口与其实现,支持多维度变化
Composite模式注重统一接口,将“一对多”的关系转化为“一对一”的关系
Decorator模式注重稳定接口,在此前提下为对象扩展功能
Façade模式注重简化接口,简化组件系统与外部客户程 序的依赖关系
Flyweight 模式注重保留接口,在内部使用共享技术对对,象存储进行优化,主要考虑性能。
Proxy 模式注重假借接口,增加间接层来实现灵活控制
行为型模式(1)
Template Method模式封装算法结构,支持算法子步骤变化,结构稳定,节点变化。
Strategy模式注重封装算法,支持算法的变化
State模式注重封装与状态相关的行为,支持状态的变化,
Memento模式注重封装对象状态变化,支持状态保存/恢复
Mediator模式注重封装对象间的交互,支持对象交互的变化
行为型模式(2)
Chain Of Responsibility模式注重封装对象责任,支持责任的变化,处理请求的责任,接受响应对象随时变化。
Command模式注重将请求封装为对象,支持请求的变化,用变量代表行为,内部会封装一个方法指针,来调用实现。
Iterator 模式注重封装集合对象内部结构,支持集合的变化
Interpreter模式注重封装特定领域变化,支持领域问题的频繁变化
Observer模式注重封装对象通知,支持通信对象的变化
Visitor模式注重封装对象操作变化,支持在运行时为类层次结构动态添加新的操作。
设计模式应用总结
设计模式建立在对系统变化点的基础上进行,哪里有变化点,哪里应用设计模式。
设计模式应该以演化的方式来获得,系统的变化点往往是经过不断演化才能准确定位。
不能为了模式而模式,设计模式是一种软件设计的软力量,而非规范标准。不应夸大设计模式的作用。
浙公网安备 33010602011771号