您的位置:首页 > 其它

设计模式思想摘要

2015-01-06 16:29 106 查看
模式是人们根据以往的经验总结出来的,可以重复使用的设计方案。也就是说,模式是一种记录在案的、经过实践检验过的设计经验,可以为在特定设计背景下的类似的问题提供一种通用解决方案。

在软件工程领域,根据开发过程的不同阶段和不同层次,主要有以下两种模式:

(1) 架构模式: 它描述了软件系统的基本结构组织蓝图,囊括了一些预定义的、职责明确的子系统,并制定了子系统之间合作沟通的规则和指南。使用它,可以方便的搭建软件系统。它是整个软件架构层次的模式。



(2) 设计模式:着眼于软件子系统层次,它在特定情境下为需要交互的组件之间搭建了的组件之间的沟通桥梁,提供了一个特定情况下可复用的解决方案。



本文主要对设计模式作一个入门级简介和摘要,并尽量做到浅显易懂。由于水平有限,欢迎各位提出宝贵意见。

设计模式可分为三大类:

(1) 创建型模式;

(2) 结构型模式

(3) 行为模式

设计原则有如下几个:

(a)Open-Close原则,即对扩展开放,对修改关闭(Open for extension, close for modification)。

开放――封闭原则是面向对象的核心所在。开发人员应该对程序中呈现出频繁变化的那部分做出抽象,拒绝对任何部分都刻意抽象及不成熟的抽象。该法则在装饰器模式中有重要体现。

(b)单一职责原则:

如果一个类承担的职责过多,就等于把这些职责耦合在一起,一个职责的变化可能会削弱或者抑制这个类完成其它职责能力。这种耦合会导制脆弱的设计,当变化发生时,设计会遭受到意想不到的破坏。

(c)里氏代换原则

一个软件实体如果使用的是一个父类的话,那么一定适用其子类。而且它察觉不出父类对象和子类对象的区别。也就是说:在软件里面,把父类替换成子类,程序的行为没有变化。子类型必须能够替换掉它们的父类型。

(d)依赖倒转原则

抽象不应该依赖细节,细节应该依赖抽象。即针对接口编程,不要对实现编程。

(e)迪米特法则

如果两个类不直接通信,那么这两个类就不应当发生直接的相互作用。如果一个类需要调用另一个类的某个方法的话,可以通过第三个类转发这个调用。在类的结构设计上,每一个类都应该尽量降低成员的访问权限。该法则在后面的适配器模式中有强烈的体现。

(f)优先使用组合,而不是继承。考虑你的设计中可能碰到的需求或用例变化,并把这种变化封装起来作为某个类的成员(组合)

1. 创建型模式概览

本文选取代表模式为单例(singleton),建造者(builder),原型(prototype)和工厂模式。

1.1 单例模式

在软件开发过程中,有许多对象只需要有一个实例存在,没必要创建多个实例给自己带来不必要麻烦。比如:线程池、cache缓存等。如何确保一个类只被实例化一次呢?

可能第一反应很多人会选择全局变量(对象)。但是全局变量以下缺点为绝大多数程序员所诟病:

(i)耦合性问题:需要访问某个特定全局变量的多个函数被该变量牢牢地"粘
结"在一起;

(ii)全局变量从某种程度上破坏了封装性,不符合面向对象的风格;

(iii) 全局变量在定义的地方就自动实例化了,不能按需实例化。

而单例模式是面向对象的一个设计模式,它确保了一个类只有一个实例存在,并开放一个获取该实例的全局访问接口给外界。

因此,对于一些需要频繁创建且内容基本相同变化较少的实例,推荐使用单例模式。

通常,一个单例模式的实现应至少有如下三个要点:

添加一个该类类型的static成员变量(指针).

向外开放一个获取示例的函数接口。在函数里会根据上述成员变量是否为NULL来进行new动作。
使该类的构造函数属性为private或protected,以避免用户直接通过定义实例化该类

使用该模式,需要注意多线程下的问题:

在使用单例模式创建对象的时候,如果多个对象同时被创建,又同时被修改或调用就有可能导致了理论值和结果值的不一致,此时线程即是不安全的.

在单例模式下,为防止多线程使用带来的不安全,最佳的办法是采用“双重检查”来避免。Java代码示例如下:



1.2 建造者模式

在软件系统中,有时面临着一个复杂对象的创建工作,通常是由很多其他的对象按一定的规则顺序组合而成;由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将它们组合在一起的规则是相对稳定(结构和顺序)。

建造者模式提供一种封装机制来隔离出构成复杂对象的各个子对象的变化,从而保持系统中的相对稳定的将这些子对象组合在一起的算法不随着需求的改变而改变。

例如,一个公司的研发中心的使命是确保各个研发项目顺利进行。不同的项目需要不同的人来做。而研发总经理不可能细节到去盯着所有项目。为了保证效率,研发总经理想了一个办法,即简政放权:

(1) 我只负责项目引入和高级人力资源安排。为不同的产品线招聘不同的产品研发总监,研发总监(Director)手下带领不同的产品研发人员(Builder)。产品研发人员(Builder)具体去负责项目实施细节。

(2) 规定每个业务部门的研发总监的工作流程都是统一的,即construct方法。

Construct的具体实现则是builder发挥作用的地方。

总而言之,流程必须是一样的(Construct),不同产品项目都要在该流程框架下工作。

其结构图如下所示:

Director的下属资源是builder,builder听从Director指挥.

在construct方法中,builder会为Director做好一切工作。

Director在不同时期会有不同项目



1.3 原型模式

从一个对象再创建另外一个可定制的对象,而无需知道任何创建的细节。并能提高创建的性能。说白了就COPY技术,把一个对象完整的COPY出一份。

1.4 工厂模式

工厂模式主要是为创建对象提供了接口。工厂模式分为三类:

(1)简单工厂模式(Simple Factory)

(2)工厂方法模式(Factory Method)

(3)抽象工厂模式(Abstract Factory)

这三种模式从上到下逐步抽象,并且更具一般性(抽象化:3>2>1)。还有一种分类法,就是将简单工厂模式看为工厂方法模式的一种特例,两个归为一类。下面是使用工厂模式的两种情况:

(1)在编码时不能预见需要创建哪种类的实例。

(2)系统不应依赖于产品类实例如何被创建、组合和表达的细节。

2. 结构型模式概览

本文选取代表模式为桥接模式、适配器模式和装饰器模式。

2.1 桥接模式

桥接模式(Bridge),将抽象部分与它的实现部分分离,使它们都可以独立地变化。
当一种抽象类型可能有多种实现方式时,一般情况我们可以考虑使用继承来解决抽象类型的多种实现,在抽象类型中定义接口,而子类负责接口的具体实现。但这种做法缺乏灵活性,由于抽象类型和子类之间紧紧地绑定在一起,使得这种关系在运行时不能再修改,这使得它难以修改、扩展和重用不利于抽象和实现解耦,而且这 也违背OOP原则:“优先使用对象聚集,而不是继承”。
标准结构图如下:



实例:



2.2 适配器模式

将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以在一起工作。有以下几种参与角色:

(a)目标接口(Target):客户所期待的接口。目标可以是具体的或抽象的类,也可以是接口。

(b)需要适配的类(Adaptee):需要适配的类或适配者类。

(c)适配器(Adapter):通过包装一个需要适配的对象,把原接口转换成目标接口。



2.3 装饰器模式

当你向旧的类中添加新代码时,一般是为了添加核心职责或主要行为。而当需要加入的仅仅是一些特定情况下才会执行的特定的功能时(简单点就是不是核心应用的功能),就会增加类的复杂度。装饰模式就是把要添加的附加功能分别放在单独的类中,并让这个类包含它要装饰的对象,当需要执行时,客户端就可以有选择地、按顺序地使用装饰功能包装对象。



3 行为模式

本文选取状态模式、观察者模式、访问者模式、责任链模式作为代表

3.1 状态模式

当一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态改变它的行为时,可考虑用到状态模式。



有限状态自动机(FSM)也是一个典型的状态不同,对输入有不同的响应(状态转移)。通常我们在实现这类系统会使用到很多的Switch/Case语句,Case某种状态,发生什么动作,Case另外一种状态,则发生另外一种状态。但是这种实现方式至少有以下两个问题:

1)当状态数目不是很多的时候,Switch/Case可能可以搞定。但是当状态数目很多的时候(实际系统中也正是如此),维护一大组的Switch/Case语句将是一件异常困难并且容易出错的事情。

2)状态逻辑和动作实现没有分离。在很多的系统实现中,动作的实现代码直接写在状态的逻辑当中。这带来的后果就是系统的扩展性和维护得不到保证。

在State模式中我们将状态逻辑和动作实现进行分离。当一个操作中要维护大量的case分支语句,并且这些分支依赖于对象的状态。State模式将每一个分支都封装到独立的类中。

实例:



3.2 观察者模式

Observer模式要解决的问题为:建立一个一(Subject)对多(Observer)的依赖关系,并且做到当“一”变化的时候,依赖这个“一”的多也能够同步改变。




3.3 访问者模式

在软件开发过程中,对于系统中的某些对象,它们存储在同一个集合collection中,且具有不同的类型,而且对于该集合中的对象,可以接受一类称为访问者的对象来访问,而且不同的访问者其访问方式有所不同。
访问者模式:表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。
1)访问者模式中对象结构存储了不同类型的元素对象,以供不同访问者访问。
2)访问者模式包括两个层次结构,一个是访问者层次结构,提供了抽象访问者和具体访问者,一个是元素层次结构,提供了抽象元素和具体元素。
相同的访问者可以以不同的方式访问不同的元素,相同的元素可以接受不同访问者以不同访问方式访问。在访问者模式中,增加新的访问者无须修改原有系统,系统具有较好的可扩展性。
顾名思义,使用该模式可以在不修改已有程序结构的前提下,通过添加额外的访问者完成对已有代码功能的提升。



例子1:顾客在超市中将选择的商品,如苹果、图书等放在购物车中,然后到收银员处付款。在购物过程中,顾客需要对这些商品进行访问,以便确认这些商品的质量,之后收银员计算价格时也需要访问购物车内顾客所选择的商品。
此时,购物车作为一个ObjectStructure(对象结构)用于存储各种类型的商品,而顾客和收银员作为访问这些商品的访问者,他们需要对商品进行检查和计价。不同类型的商品其访问形式也可能不同,如苹果需要过秤之后再计价,而图书不需要。可以使用访问者模式来设计该购物过程。

访问者模式包含如下角色:

抽象访问者(Vistor): —为该对象结构中ConcreteElement的每一个类声明一个Visit操作。该操作的名字和特征标识了发送Visit请求给该访问者的那个类。这使得访问者可以确定正被访问元素

的具体的类。这样访问者就可以通过该元素的特定接口直接访问它。

具体访问者(ConcreteVisitor): 实现每个由Visitor声明的操作。每个操作实现本算法的一部分,而该算法片断乃是对应于结构中对象的类。ConcreteVisitor为该算法提供了上下文并存储它的局部状态。

这一状态常常在遍历该结构的过程中累积结果。

抽象元素(Element):定义一个Accept操作,它以一个访问者为参数。

具体元素(ConcreteElement): 实现Accept操作,该操作以一个访问者为参数。

对象结构(ObjectStructure):能枚举它的元素。可以提供一个高层的接口以允许该访问者访问它的元素。可以是一个复合或是一个集合,如一个列表或一个无序集合。

3.4 责任链模式

责任链模式是构建面向对象软件体系架构的一个重要组成成分之一,它的光环不仅把软件系统从接受者和发送源这对冤家中解脱出来,更重要的是提高了代码的可读性和灵活性。

《设计模式》中给它的定义如下:使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。



以下是责任链模式的应用范围:

1)有多个的对象可以处理一个请求,哪个对象处理该请求运行时刻自动确定。

2)你想在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。

3)可处理一个请求的对象集合应被动态指定。

责任链模式有三个角色组成:

1) 抽象处理者角色(Handler):它定义了一个处理请求的接口。当然对于链子的不同实现,也可以在这个角色中实现后继链。

2)具体处理者角色(Concrete Handler):实现抽象角色中定义的接口,并处理它所负责的请求。如果不能处理则转发请求给它的后继者处理。

3)客户端:当客户端发出一个请求时,该请求沿着责任链向后传递,直到有对象负责处理为止。

ConcreteHandler将自己的后继对象(向下传递消息的对象)记录在自己的后继表中,当一个请求到来时,ConcreteHandler会先检查看自己有没有匹配的处理程序,如果有就自己处理,否则传递给它的后继。

注:本文参考了部分网络上信息,作者不详。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: