Chinaunix首页 | 论坛 | 博客
  • 博客访问: 89046
  • 博文数量: 26
  • 博客积分: 920
  • 博客等级: 准尉
  • 技术积分: 235
  • 用 户 组: 普通用户
  • 注册时间: 2006-06-28 10:50
文章分类

全部博文(26)

文章存档

2015年(2)

2011年(1)

2009年(10)

2008年(2)

2007年(1)

2006年(10)

我的朋友

分类: 项目管理

2006-06-30 10:25:50

1
什么是设计模式
设计就是解决方案 对某个问题的解决
如果某个解决方案对某类问题都很有用 这时就把它总结出来
这就产生了设计模式
2
设计模式的要素
1 名称 用于助记 .道可道 非常道 名可名 非常名 起这个名只是表示形象表示这个模式
2 问题 这个模式可以解决什么问题
3 解决方案 这个模式怎样解决这个问题的 步骤与方法
4 效果 使用这个模式与不使用这个模式有什么区别 它有什么优点和缺点
开发程序应该知道 一个问题可以有多种解法 好的解法都可以找到很多种 每种都有乱缺点
所以写代码时不要死记方法 应该活用 软件开发并不是小学老师教的不是好就是坏
3
MVC模型
每本设计模式或是面向对象的书都会见到MVC
它们或是表现自己深沉或是借助它来表现最原始的设计方法
所以 懂MVC还是有必要的
MVC(Model/View/Controler)是模型/视图/控制器 最先出现于smalltalk语言
在面向对象中 模型就是对象 视图是对象的表示 控制就是对象的控制接口

class A{
private:
int i;
public:
void add();
void showinhex();
void showinbin();
};
A a;
a就是模型
add就是控制器 通过它来改变对象的状态
用户对i的解释就是视图的表现 也就是 数据在那里 用户可以根据自己的需要使用不同的方法来解释i 不同的表现并不会修改对象的状态 这些表现只是根据自己的需要来表现对象的状态
另外 视图并不一定是显示 而是自己对这个状态的解释
将控制与表现分开是一种最基本的设计模式 可以使双方独立的变化 一方变化不会影响另一方
如果使用vc开发的话 会懂得文档/视图模式 这就是mvc的一个应用表现
4
设计模式的中主要方法与目标
软件是在不断进化的 需求在不断改变 所以软件应该适应变化
设计模式是为了让软件更加可适应变化 有更多的可复用性 也就是有变化时你不用从头重写一次这个软件
说到这里不得不提一下XP方法
XP方法(极限编程)的口号就是 简单就是最好 你今天写的到明天将不再使用
提倡简单(不用为现在不使用的功能而添加代码),
沟通(配对编程与不断的进行开会沟通),
测试(保证已开发部分的稳定性),
重构(保证软件结构适合变化)
另外提倡小设计,这些小设计只是为了完成当前模块,为了方便当前沟通而使用 并不会进行归档
与传统设计需要有很多的设计文档(概要与详细设计 )并且需要进行同步不同 XP认为没有同步的文档比没有文档更差 不提倡文档也是XP的一个很特别的特点
极限编程当发现软件结构不适合变化时 就建议进行重构 保证软件结构的完善
一般人可能会因为重构而认为它不提倡设计模式或软件设计
认为如果软件写不好就进行简单的重写
但是 极限编程也是讲究设计的 当然这个设计只是针对当前模块
我觉得XP方法就像是积木的办法 先把一块一块的积木搭好 每到一个阶段就进行检查 保证质量 最后组成一个大系统
5
要使一个软件更适应变化 就应该封装变化 让变化的影响最小
另外就是封装复杂性 提供简单的接口
通常有下面几种形式
1 松耦合
软件分成模块 大的软件都是由一个一个小模块组成的 小模块还可能再由更小的模块组成
一个模块就是相互间联系紧密的变量与函数 或对象 不同模块间关系比较松散
模块间关系越松散 这样修改一个模块时 对其它模块的影响就越小 这样可以把变化限制在一个模块内
2 针对接口编程 而不是针对实现编程
具体来说就是 你只要定义这个接口有什么功能 不用定义它是什么实现的
当其它模块需要这个功能时 只要调用这个接口就可以 它不必了解你的实现机制
当你换一个实现时 就不会同时修改其它模块
3 继承 组合 委托与多态与参数化
继承是有父子关系的对象的具体化
组合就是把有相互关系的对象放在一起 由它们提供某一个或一组功能 这功能需要它们共同配合才能实现
委托是一个对象把一个请求转交给另一个对象执行 相当歌手与经济人的关系 经济人是委托者 歌手是执行者
多态就是实现的可替换性 你可以在运行时使用另一种实现来代替当前实现而不必修改代码
参数化就是根据不同类型的参数提供不同的功能 如c++中的模板机制 如vector类
这些是设计中常用的方法 同时也是c++语言提供的功能 应该理解
6
c++语言的特点
想使用设计模式 就应该对某种语言进行具体的了解 这样才能更好的使用这种语言来进行设计
c++语言引入的对象的概念 为的是更符合人们对自然的认识 所以学习c++或使用c++时 不应该把它看成一种机器语言 而应该看成是自己思考的办法
下面列出c++语言提供的功能

1 类
类就是某一类东西 类定义了这类东西共同的内容与操作 当然这些内容与操作是你所感兴趣的 衣服 是一类东西 它有很多特点 如果你只关心它的样式 那可以只定义对样式的操作
class clothes{
private:
int type;
public:
void settype();
int gettype();
};

2 封装
某些内容 你只想自己使用 而不想让其它对象能够访问 这时就可以使用封装
一共有三种封装方式rivate public protected 请参考语言书
另外 如果你想让某个类能访问另一个类的内容 这时使用friend
在c++中,struct与class的功能是一样的 都可以有接口(函数) 所不同的是
struct 默认时是public class默认是private
3 继承
继承是进行类的具体化
如上面的衣服 有上衣与裤子 但是它们都有衣服的特征
所以可以共用衣服类内容 再定义自己特有的内容与接口 这也是一种代码复用
继承也有三种继承方式rivate public protected 常用的是public 当然不要忘记还可以使用其它类型的继承方式

4 多态
多态就是实现的可替换性 又分成运行时多态与编译时多态
如果运行时才替换实现 叫运行时多态 如果编译时就已经知道使用哪个实现 叫编译时多态
实现多态的方法有:
方法的重载(function overload)同一个可见范围中 函数名相同 但是参数不同 这时不需要virtual
方法的覆盖(function override) 在有继承关系的类中 子类提供自己的实现对父类实现进行覆盖
注意这时 必须在父类中需要被覆盖的方法定义前加上virtual
5 this
this 使用于对象中 表示当前对象
6 虚函数与纯虚函数 抽象类
虚函数就是在函数定义前面有virtual的函数 表示它的实现可以被覆盖 也就是用于多态为的方法的覆盖
纯虚函数就是只定义不提供实现的函数 它前面有virtual 最后是=0 如下
class A{
public:
virtual void add() = 0;
};
这时add函数叫纯虚函数 它用于定义接口
有纯虚函数的类叫抽象类 如上就是类A 它不可以定义对象 当然可以定义对象指针指向子类
7 模板
模板就是使用template关键字定义的类或是方法 表示某类操作除了类型不同外 处理方法相同
参考语言书
8 RTTI(运行时识别)
运行时识别对象类型 或是进行对象的向上转换(从基类转换到子类 见dynamic_cast)

其中虚函数与纯虚函数是定义接口的方法 在设计模式中使用的比较多
如果你想让某个接口晚实现的话 就使用它们 理解它们对理解设计模式会有很大作用

7
继承与组合
继承就是有父子关系的类
组合是把一些对象放在一起使用
继承破坏了封装性(子类可以看到父类的实现细节 父类实现的改变是很难的事情)所以优先使用组合
另外使用继承时 应该把析构函数定义成virtual的
虚析构函数保证子类被父类指针调用 并释放时可以正确析构
8
设计模式的分类
根据模式的目的(用来完成什么工作的) 可以把设计模式分成:
创建型模式:
结构型模式:
行为型模式:
三种
创建型模式与对象的创建有关 结构型模式处理对象或类的组合 行为型模式对类和对象怎样交互和怎样分配职责进行描述
根据模式的作用范围(是处理类还是处理对象的) 可以分成
类模式
对象模式
两种
类模式处理类与子类的关系
对象模式处理对象间关系
创建型类模式将对象的部分创建工作延迟到子类 而创建型对象模式将它延迟到另一个对象中
结构型类模式使用继承机制来组合类 结构型对象模式描述了对象间的组装方式
行为型类模式使用继承描述算法与控制法 行为型对象模式则描述一组对象怎样协作完成单个对象无法完成的工作

总的类说 创建型模式就是怎样构造一个对象的方法
结构型模式就是怎样使用组合的方法
行为型则是怎样分配类或对象的功能 及怎样调用它们的算法
类模式与类继承相关 对象模式与对象间关系相关
明确了这些基本的功能后 后面看设计模式时会更清楚一点
9
下面开始按目的来看设计模式
第一个是创建型模式
上面说到创建型模式是为了怎样构造一个对象而产生的
创建型模式抽象了实例化过程 帮助一个系统独立于如果创建 组合和表示它的那些对象
类创建型模式使用继承改变被实例化的类 对象创建型模式将实例化托给另一个对象
在这些模式中有两个不断出现的主旋律
1 它们都将系统到底使用哪些具体类信息封装起来
2 它们隐藏了这些类的实例是如何被创建和放在一起的
整个系统关于这些对象知道的就是由抽象类定义的接口
(注意看到抽象类 也就是上面的原则中的针对接口而不是实现编程 使用抽象类可以把这个类的具体怎样实现放在子类中进行 而调用者只需要知道这个类有这样的功能就行了 举例如打印 调用者只要知道可以打印出来就可以 而不必知道是在屏幕上打印还是显示器上打印)
所以 创建型模式在什么被创建 谁创建它 它怎样被创建 及何时创建提供了很大的灵活性
创建型模式有如下五种

类创建型模式
1 工厂方法 factory method 定义一个用于创建对象的接口 让子类决定将哪一个类实例化
当你编译时不知道要实例化哪一个类 这时可以把类的创建放在子类中 提供专门的接口创建一个对象
在书中例子是application与document,application对不同的应用要使用不同的document,所以编译时不知道需要哪个 document子类 这时可以在application中提供createdocument接口 返回一个document对象指针
因为返回的是一个对象 所以叫工厂方法
对象创建型模式
2 抽象工厂abstract factory 提供一个创建一系列相关或是相互依赖对象的接口 而无需指定它们具体的类
如 界面的显示有不同的控件 如form button等 而这些控件都与具体的系统相关 也就是说 要使用其中一个就得和这组控件相配合
这时你定义一个抽象工厂类 把这些控件的构造都放在这个抽象工厂类中 当需要一个控件中 调用这个抽象工厂类提供的接口就可以了
但是 要怎样知道这些控件有什么功能呢 每个控件定义一个抽象类 声明它提供的功能
后面使用抽象类的指针来调用这个控件 问题解决
因为它像一个工厂一样生产出相似产品 而这些产品都是抽象类 所以叫抽象工厂
 

3 生成器builder 将一个复杂对象的构建与它的表示分离 使同样的构建过程可以创建不同的表示
builder模式有两个参与者 一个是生成器(builder) 负责生成新对象 另一个是导向器(director),
导向器分析转入的信息,处理后根据信息的内容调用生成器 生成器根据不同的信息内容生成不同的对象来保存这些信息
在书中是一个rtf文字阅读器 分析rtf模式文件后根据不同的信息创建不同的对象保存这些信息

4 原型prototype 用原型实例指定创建对象的种类 并通过拷贝这个原型来创建新的实合金
技术上的实现:根据一个已有的对象信息clone产生新对象
5 单件singleton,保证一个类只有一个实例 并提供访问它的一个全局访问点
这个技术没有什么难度的 就是需要把构造函数都定义成非public,只要构造函数非public就不可以被再构造 所以后面可以使用其它方面创建一个实例
书上使用静态变量与静态函数的方法来提供全局调用接口
 
10
后面再详细的介绍各设计模式的使用方法 与使用条件 另外还有优缺点
当然这些在书上都有

结构型模式
结构型模式是使用组合来实现设计的模式 通过组合类或对象来得到更大的结构(想想变形金刚tongue.gif)
类的组合是使用多重继承 把两个或以上的类组成一个类 新类有所有父类的性质
对象的组合是使用对象成员变量的办法来实现新的功能

根据上面的设计要求 优先使用组合而不是继承 所以理解结构型模式也是很重要的
结构型模式有如下八种
结构型类形式
适配器 adapter 将一个类的接口转换成客户希望的另外一个接口,使因为接口不兼容的类可以互相工作 这里是通过继承的方法实现接口的转换
结构型对象模式
适配器 adapter 将一个类的接口转换成客户希望的另外一个接口,使因为接口不兼容的类可以互相工作 这里是通过成员变量的方法来实现
上面两个都是适配器 功能都是因为现有的接口无法满足要求 需要转换
分开只是两个实现适配器的方法不同 一个通过继承 另一个通过成员变量

桥 bridge 将抽象部分与实现部分分离 使它们可以独立的变化
例子中 抽象就是window,这是一个窗口类 提供窗口的功能 如画框等高级功能
实现就是windowimp 是各平台上的具体显示办法的基类 主要包括画点 画线 画文字 windowimp只有低级的显示功能
window调用windowimp来实现自己的窗口功能
针对各平台的窗口子类从windowimp继承 并实现对应的windowimp功能
在这里抽象与实现的解释
window认为是抽象类 是因为其它类都是使用window类提供的高级显示功能 它们不与windowimp打交道
windowimp的子类提供具体的低层显示功能 被window类对这些功能进行封装提供更高级的显示功能
所以认为windowimp是实现类 window类是抽象类
 
组合 composite 将对象组合成树型结构以表示"部分-整体"的层次结构.composite使得用户对单个对象和组合对象的使用有一致性
对象和对象的组合在接口上一样 所以对它们的操作也一样 方便用户的使用
装饰 decorator 动态的给一个对象增加另外的职责
注意 是给一个对象而不是一个类增加功能 ,我们知道给类增加功能可以使用继承的办法,但是 给对象就没有必要那么麻烦 可以把这个对象嵌入另一个对象中 由另一个对象增加功能.我们称这个嵌入的对象为装饰
这个装饰与它装饰的组件接口一致 因此对使用这个组件的客户透明 当装饰接收到客户请求时 把请求转发给组件 并在转发前后增加一些附加的操作
由于透明所以可以递归嵌套多个装饰
看书上例子 装饰类与它被装饰的类都是从同一个父类中继承的 另外装饰类定义了指向被装饰类的指针
 
外观 facade 为子系统中一组接口提供一个一致的界面 facade模式定义了一个高层接口 这一接口使得这个子系统更易使用
这个 通俗的说 就是把子系统中接口进行了封装 只提供简单的接口 客户不必了解这个子系统中有什么对象 有什么接口 只需要那套简单接口就可以了
如果使用过库的话 那对这点还是可以理解的 如socket库 你只知道有那些简单接口(socket,bind ,send,recv)其它复杂部分已经封装了
享元 flyweight 运用共享技术有效支持大量细粒度对象
出现原因: 有些系统的对象数量太大而难以直接使用对象建模
每个对象都有内部状态与外部状态 使用flyweight时 对象的内部状态保存在flyweight中 外部状态在调用时传入 这样只需要一个flyweight对象 就可以保存很多的内部状态了 而不必生成大量的对象
书上使用文字编辑器举例 字符内容就是内部状态 字符显示位置就是外部状态
 
代理 proxy 为其它对象提供一种代理以控制外界对这个对象的访问
如果你不希望外界访问某个接口 或是对某个接口在某种条件下显示另一个结果 这时就可以使用代替
这个和我们平时使用上网代理一样的道理 也很相似
 
11
行为模式
行为模式涉及到算法和对象职责间分配 行为模式不仅描述类或对象的模式 还描述它们之间的通信模式 这些模式刻划了运行时难以跟踪的复杂的控制流
行为类模式使用继承机制在类间分派行为 行为对象模式使用对象复合而不是继承 描述了它们怎样配合完成其中任意一个对象都无法完成的任务
下面把包括的模式说明一下
行为型类模式
解释器interpreter 给定一个语言 定义它的文法的一种表示 并定义一个解释器 这个解释器使用该表示来解释语言中的句子
最简单的使用是读配置文件了 最复杂的使用是用它来进行yacc或是lex语法文件的解释
模板方法template method 定义一个操作中的算法的骨架 而将一些步骤延迟到子类中.template method使可不改变一个算法的结构即可重定义该算法的某些特定步骤
书上例子是OpenDocument函数,它定义了这个函数的框架 也就是这个函数的主要步骤
但是CanOpeDocument和 DoCreateDocument 在子类中实现 这样可在子类中定义具体的操作方法
总结: 在父类中定义了操作的基本步骤 但是每个具体步骤的动作却在子类中实现 这就叫模板

行为型对象模式

责任链 chain of responsiblity 使多个对象都有机会处理请求,从而避免请求的发送者与接收者之间的耦合关系.将这些对象连成一条链 并沿着这条链传递请求 直到有一个对象处理它为此
如果你开发过界面编程的话 如windows GUI编程 那消息的处理方法就是职责链的形式 一个消息在消息处理对象组成的链中传送 如果哪个对象愿意处理它就处理

命令 command 将一个请求封装为对象 从而可使你对不同的请求进行参数化 对请求排除或是记录日志 及支持可撤消的操作
这可以认为是回调在面向对象中的扩展
书中例子是界面操作请求封装成对象的形式 每个对象内说明要进行的操作 如past请求对象的操作是从剪切板粘贴内容

迭代器 iterator 提供一种方法顺序访问一个聚和对象中各个元素 而又不暴露该对象的内部表示
如果使用过stl 的话 对这个就容易理解 每个容器都定义了一个迭代器用于访问这个容器内的各元素 同时使用iterator时不必知道各元素在容器内是怎样保存的 如是顺序保存还是链表方式保存的 没有暴露封装性

vector a;
...赋值
for(vectir::iterator iter = a.begin();iter != a.end();i++) 操作;
从上面看不出元素是怎样保存的 就是换成list一样可以成功运行
 

中介者 mediator 用一个中介者来封装一系列的对象交互.中介者使各对象不需要显式的相互引用,从而使耦合松散,而且可以独立的改变它们的交互
如果一个模块中有多个对象 并且它们又需要相互联系 这时就会降低它们的可复用性
这时可以引入一个中介者
中介者负责控制和协调一组对象间的交互 ,中介者充当一个中介 以使组中的对象不再相互显式引用 也就是 这些对象只知道中介者 从而减少相互引用的数目
书上例子是一个对话框 对话框窗体知道它包括的所有组件 并协调它们间的交互 看那个图很好理解 所有的对象都把消息传到中介者那里 再由中介者把这消息中转给其它对象
 
备忘录 memento 在不破坏封状性的情况下 捕获一个对象的内部状态 并在此对象之外保存这个状态 这样可以将此对象恢复到原先保存的状态
这个技术类似于数据库技术中的检查点 或是事务
备忘录的提出是为了提供类似事务的功能 对某些逻辑上取消比较麻烦的对象使用备忘录来直接还原
备忘录模式的参与者有两个对象 一个是备忘录(memento) 用于保存状态 另一个是原发者(originator)是被保存状态的对象

观察者 observer 定义对象间的一种一对多的关系 当一个对象的状态改变时 所有依赖于它的对象都得到通知并被自动更新
这一模式有两个关键对象 一个是目标(subject) 另一个是观察者(observer) 一个目标可以有任意多个依赖于它的观察者 一旦目标状态改变 所有的观察者都得到通知 每个观察者都向目标查询以同步状态
典型的应用是界面开发中数据改变时界面的显示了
状态 stat 允许一个对象在其内部状态改变时改变它的行为 对象看起来似乎改变了它的类
这个 简单的说就是对象的行为由它当前的状态决定 如对象内有tcpsend ,如果还没有连接那调用时是返回错误 如果已连接那调用时是返回成功

策略 strategy 定义一系列的算法 然后把它们封装起来 使它们可以相互替换.本模式使算法可独立于使用它的客户而变化
目标是为了可在运行时动态改变算法

访问者 visitor 表示一个任务于某对象结构中的各元素的操作.它使你可以在不改变各元素的类的前提下,定义作用于这些元素的新操作
具体来说 就是把对象的操作与数据分开 把操作放在一个独立的对象--访问者中
当访问者访问对象时,把访问者传送给当前访问对象
被访问对象接受访问者时 向访问者发送一个包含自身信息的请求
访问者对被访问对象提供的数据进行操作

 
阅读(882) | 评论(1) | 转发(0) |
给主人留下些什么吧!~~