再回首——行为型设计模式
最后更新于:2022-04-01 09:30:43
## 行为型
设计模式被分成三大类,创建型,结构型,行为型。具体要阐述为什么这么分,这个问题,暂时解决不了,但是我们能做的是,合理的运用它。对于行为型设计模式,区别于其他两种的就是:它侧重的是对“方法”的操作。
下面是对几个行为型的设计模式的理解。
## 一、模板方法
1、概述
将一个操作的算法的骨架和具体算法实现分离——解耦 骨架在父类中,算法延迟到子类实现,就可以有N多实现——多态的体现
回顾一下类图,看看模板解决的问题:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-18_56c5ce760b191.jpg)
**模板的眼**:**;解决代码重复,会算法有既定顺序。**
-
使用**继承**,解决代码重复的问题,重复的代码抽象成一个基类,做成模板;杜绝子类出现不变的代码,模板就是为了给子类瘦身。
-
**基类中有代码实现**:这里抽象出来不单单是某几个方法声明,方法中是可以有代码实现的。只要是会发生重复的就放到父类中。
-
**算法顺序**:父类中(模板)必须有一个既定的方法来执行算法顺序——算法的骨架,是既定的
-
子类只是不同算法的实现父类,不改变算法结构
**2、改造模板----算法骨架灵活**
**算法的骨架是既定的,可以修改吗?要不要修改?**
这里的算法骨架顺序跟建造模式类似,顺序基本是稳定。怎么说呢?
一般使用这两种模式的“事物”,它的构成顺序都是稳定的,不是经常需要变动的,这是一个前提,但是并不是说,这个顺序一定不可以改变,为了灵活编程,可以通过委托来实现。往委托队列注册的过程就是排序的过程,委托是一大块,不多说了。
**子类可以扩展父类吗?**
当然是可以扩展的,因为是继承呀。但是个人认为没有多大意义。如果只是为了添加方法到子类中,跟复写方法无关联,那就是有点破坏单一原则的味道了。
**3、建造模式VS模板模式**
相同:都有既定的顺序,仅此而已。
不同:两个设计模式的适用范围不同,解决问题不同,一个是创建型,一个是行为型,其他就不用啰嗦了吧。
## 二、观察者
**1、观察者的几点认识**
-
一对多的依赖关系
-
多个对象观察一个主题
-
主题发生变化,观察的对象随之变化
主题与观察对象,观察对象之间都是陌生的。一方负责变化,通知;一方负责接收,做出对应变化。
生活中的实例:订阅网站。
通过类图来分析观察者的优缺点:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-18_56c5ce761f1c8.jpg)
**优点**:解耦是观察者的一大特色。通过依赖倒转,面向接口编程的思想,给多个观察者增加父类接口,让被通知者成为主动观察者,来实现观察者和主题的衔接。
**不足**:主题只能和一种类型的观察者进行关联——从途中可以看出所有的观察者都是继承自一个父类:observer,所以这些观察者都是一个父类,要想增加多个类型的观察者,便有心无力了。
**2、观察模式进阶委托**
那要是我想通知多个类型的观察者怎么办?答案还是——**委托**
1.
想要通知其他类型的观察者,需要如下改动:最大的局限就是这个抽象的接口——observer,那么就把它去掉。
1.
抽象类不存在,那么主题中通知的方法(notify)就没有意义了,也去掉。
1.
将动态通知观察者的权利从主题移交给调用者。——主题和观察者彻底解耦。
1.
声明委托,在调用短端注册各种类型的观察者,执行调用。
## 三、策略模式:封装算法,算法变化不影响用户使用。
(具体的在上篇博客中已经写到了)
**总结:**行为型的设计模式还有:职责链,命令,解释,迭代,中介者,备忘录,状态,访问者模式。这里只写了三个,其他的几个没有这几个上手。对于剩下的几个模式:我们本着一个这样的原则:what?when?how?
再回首,策略、简单工厂是否依然?
最后更新于:2022-04-01 09:30:40
这篇博客是好久之前就打好的草稿,可是一直就拖到了现在,刚好在保定上课,笔记本也排不上大用场,翻看手机,终于捡起了这些草稿,决定写完。
再说设计模式之前,我们先说说开闭原则。
## 一、开闭原则(ocp)
以前读这些官方解释,总是觉得很官方,现在在读,觉得句句经典。和大家共享
遵循开闭原则设计出的模块具有两个主要特点:
(1)对于扩展是开放的(Open for extension)。这意味着模块的行为是可以扩展的。当应用的需求改变时,我们可以对模块进行扩展,使其具有满足那些改变的新行为。也就是说,我们可以增加模块的功能。
(2)对于修改是关闭的(Closed for modification)。对模块行为进行扩展时,不必改动模块的源代码或者二进制代码。模块的二进制可执行版本,无论是可链接的库、DLL或者.EXE文件,都无需改动。
**注意**:开闭原则是针对一个模块来说的,不然,开闭毫无意义。
这两个模式是我们容易混淆的,因为从类图是看,基本是一样的。以前博客中都已经介绍过这几个设计模式,这里不做详细介绍,这篇博客是最近翻看笔记时的一点感触,来说说这两个设计模式的不同。
## 二、你还是你,我还是我——策略VS简单工厂
1、问题域不同
首先看一下两个模式的定义:
**策略模式**
策略模式(Strategy):它定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换。策略模式让算法的变化不会影响到使用算法的客户。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-18_56c5ce75ba397.jpg)
**简单工厂**
简单工厂模式(Simple Factory Pattern)属于类的创新型模式,又叫静态工厂方法模式(Static FactoryMethod Pattern),是通过专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-18_56c5ce75e6e94.jpg)
最主要区别是就是:策略模式为行为型设计模式,而简单工厂为创建型模式。两个设计模式解决的问题域本就不同,侧重点不一样,放开眼界,学习这两个模式,不可一起咬住不放。
2、开闭原则
策略模式的**特点:**创建,选择不同策略的责任在客户端。
如果有扩展着怎么办呢?需要如下变动:
1:策略端增加新的策略类,无需改动已有的策略端代码————开闭原则
2:界面增加新策略字符串,**客户端进行调用修改**————客户端是无论如何都是要进行修改的。
那么我看简单工厂的**特点:**选择,创建的责任在算法端
所以如有扩展,需要如下变动:
1:算法端增加新的算法类,**调用算法的修改 还在 算法端**,需要改动两处————违反开闭原则
2:客户界面增加选择新的算法的字符串————客户端是必定要修改的
这就是区别二,一个遵循了开闭原则,一个没有。还有一点就是,并不是不遵循开闭原则就一定不好,具体还是要看需求,具体对待。
宏观上问题域不同,细节上,是否需要遵循开闭原则,这两点足以让我们可以适时的选择清楚简单工厂和策略模式了。
**三、策略 同 简单工厂**
两个模式不考虑问题域的话,只看类图,都是通过继承来实现子类的扩张,都能解决动态变化的功能。因此都有一些共性:
**优点:**
1、 简化了单元测试,因为每个算法都有自己的类,可以通过自己的接口单独测试。
2、 避免程序中使用多重条件转移语句,使系统更灵活,并易于扩展。
**缺点:**
1、 因为每个具体策略类都会产生一个新类,所以会增加系统需要维护的类的数量。
**总结:**其实23个设计模式,非要交合在一块说,那么我想说:都是相近的,因为他们统一的宗旨是:抽象,继承,多态,封装,所以,设计模式每个和每个都有想象的地方,或许正因为是这样,才有的设模式的三大类:创建型,结构性,行为型之分,然后,每个小的区域内,又有各个不同的侧重点。
这是我学习设计模式的一点小感悟:
学习设计模式:不谋全局者,不足以某一域。
使用设计模式:知其然,知其所以然。
何为抽象?你有本末倒置吗?
最后更新于:2022-04-01 09:30:38
## 一、何为抽象?
提到抽象,你会想到什么?是这些吗?
1.
抽象是面向对象的基础,有了抽象才会有面向对象的三大特征:继承,封装,多态。
1.
层与层联系要依赖抽象,上层依赖抽象,下层也要依赖抽象。
1.
总之一句话,编程就是要依赖抽象。
等等这类的话,我们朗朗上口。那么回头再来看这些,它到底是什么?
它不是抽象,它是抽象的一些体现,也就是说这都是抽象后的结果,抽象的优点好处。作为程序员的我们要的就是抽象带来的这些结果,但是我们更重要的一个任务是,如何做出“抽象”?把抽象敲出来,有代码来体现。对于程序员来说,只有将想法落实到代码上才是编程,是有质量的编程。
## 二、为什么抽象
**那么何为抽象?**
1. 有相同就抽象
1. 不同领域要联系需要抽象
**为什么要抽象?**
**抽象的最直接目的:为了变化,方便交流**。
这两个问题往往是分不开的,没有目的的抽象就是无意义的工作。所以在这里一块说说我的看法。
先说第一点:有相同就抽象。遇到几个类有相同的特性:方法也好,属性也好,就可以去使用抽象了。
1、变动少。凡是这种整体的修改,如果有抽象的父类,只要在父类中修改,子类继承即可。省去该多处的麻烦,最怕的是没有改完。
2、接口统一,多种选择。有抽象,就意味着子类可以有多种实现;多态在这个时候就是最完美的诠释了抽象的神奇。对外是统一的,但是却可以选择不同的“子类”,达到不同的效果。
3、扩展是极方便的。当前存在的类的实现不能满足我的需要,我只需要增加一个继承抽象的子类,定义需要的新实现就可以达到目的,与“开闭原则”吻合。
其次是:不同领域要联系需要抽象——解耦紧密相邻的关系
在普通的不过的是,不管什么牌子的USB数据线,都可以接通任何牌子的电脑的USB接口。
这里的接口就是抽象出来的一套规范,只要不同的“领域”把要接触的“接口”规定好,就可以按照这个接口的约定去进行各种实现了。在编程中更是,为了编码变得简单,为了系统系能好,为了合作开发,一个系统被分成了“N”层,分层的目的,是为了解耦,要直接联系的两个类通过一组约定,有直接联系编程间接联系。在遵守约定的情况下,进行各自的开发。互不影响。
如果没有接口,直接发生关系就会这样:
1、每走一条线,都需要从头走到尾;如果一处做不好,就无法运行;
2、一处方法发生变动,特别是底层方法,调用这个方法的所有的类都需要变动。
3、需求变动,要求更换以前类的执行过程,好比商场打折,有多种选择的情况下,只能增加类,在需要的时候,临时更改调用那个,对于发布的系统,这可不能算一种解决方案。
(这里的领域,你可以理解成层。层的概念也是可大可小,没有严格限制,有代码经验的人根据经验来划分自己的层)
从抽象的由来就可以看出,抽象出现就是为了“交流”。如果说这个类在系统中永远只是这样,不会扩展,不会被传承,不会发生变化,那么就没有抽象的必要了,因为它是“唯一的”。 不变化,交流不影响,要变化,还要交流就必须抽象。
## 三、抽象的体现形式
**1抽象成基类**
大家熟知的形式。将相似的几个类中可以抽象的成员拿出来,形成他们的基类。
基类也可分为抽象类和接口,抽象类和接口的区别在于:基类是对属性和方法的抽象,侧重是对“代码重复”的解决;接口是对方法的抽象,避免“方法重复”。
2、**合并同类项,不增加父类。**
这种方式,是最近学习设计模式的时候突然的理解。在工厂模式到抽象工厂模式改造的过程就是这样。大家看分析。
工厂模式:只建立一个产品:Button。类图如下:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-18_56c5ce7553fb9.jpg)
建立另一个产品:Text。类图如下:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-18_56c5ce7570e5e.jpg)
观察两个类图,一模一样的工厂模式,一次却只能实现一个产品,要是实现两个产品,就需要把两个图结合起来,那就成“大物”了。让咱们来合并同类相吧。
1、两个Factory,合并,方法+1;两个UnixFactroy,合并,但是方法+1;两个WindowFactory,合并,方法+1。之所以可以合并时因为他们本质一样。
2、但是具体的Button,text抽象类和具体的实现类可不能合并了,他们本质不一样。
看合并的结果:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-18_56c5ce759f90a.jpg)
**不增加父类,增加第三者。**
这是最简单的一种抽象,也是常用的一种,估计有些人没把它当成抽象。
把多个类中用到的相同方法拿出,作为公共方法,放到第三个类中。这是我们经常用到的,和其他类不建立继承或实现关系,需要的时候就引用。当然在某种情况下,这个第三个类可能被抽象成接口来对待,具体的不做讨论,情况太多,具体对待。
## 总结:
抽象来源于个体,多了才抽象。
现有的子类,抽象后,才有的基类。
分析设计模式的时候,从简单入手,画着画着,就有了父类,有了继承,明白的抽象的存在;写代码也是,先写,写着写着就有了抽象类,有了接口。
以上就是这些天对抽象的思考。欢迎大家指正。
登陆也需要装饰——机房收费系统装饰模式实战
最后更新于:2022-04-01 09:30:36
## 一、装饰模式概述
装饰模式的用途,顾名思义,现实生活中我们需要一些装饰品来装饰人,或者物。淡然没有这些装饰,物体和人依然是存在的,这就意味着装饰品并非必须的,我们有时需要一个(项链),或许有时需要二个(项链,耳钉),再或者需要更多(项链,耳钉,戒指……),用一句话概括:装饰模式就是**动态地给一个对象添加一些额外的职责**。
关于更多装饰模式理论:详见博客:[装饰模式——结构型设计模式之四](http://blog.csdn.net/wangyongxia921/article/details/8480229)
## 二、用户装饰模式来装饰机房收费系统登陆
任何一个系统登陆都需要经过验证,这里以机房收费系统为例进行说明。
首先登陆到一个系统,为了系统安全性,我们应该从用户名,密码,来考虑。
登陆系统时需要提供匹配的用户名和密码,然后通过系统的验证通过后,方可登陆成功。
先要验证用户名,是否存在,然后验证用户密码是否正确,如果有登陆状态,最后验证登陆状态是否可以登陆。
在这里,我们可以把用户名,密码,登陆状态看成四对“登陆这个对象”的装饰,使用装饰模式。
先看装饰模式的类图:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-18_56c5ce724b74f.png)
然后结合登陆使用的装饰模式图如下:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-18_56c5ce753a2e1.jpg)
这里省略掉了接口:component。因为这里的concreteComponent只有“login ”,所以没有必要在定义一个接口。我们让TestUser 继承Login 类,其实,TestUser ,也不可以不继承Login ,这样,“Login ”和TestUser 之间就是关联关系,也可以实现。
具体代码实现:
Login类的如下:定义了一个抽象方法
~~~
Public MustInherit Class Login
''' <summary>
''' 检测用户是否可以登陆
''' </summary>
''' <param name="useren">用户类</param>
Public MustOverride Function TestLogin(ByVal useren As UserEntity) As Boolean
End Class
~~~
TestUser 类如下;
~~~
''' <summary>
''' 检测用户类_继承自login 类
''' </summary>
Public MustInherit Class TestUser
Inherits Login
Public declogin As Login
''' <summary>
''' 设定要装饰的对象
''' </summary>
''' <param name="declogin"></param>
''' <remarks></remarks>
Public Sub SetObject(ByVal declogin As Login)
Me.declogin = declogin
End Sub
''' <summary>
''' 检测用户是否可以登陆
''' </summary>
''' <param name="useren">用户类</param>
Public Overrides Function TestLogin(ByVal useren As UserEntity) As Boolean
'定义返回值
Dim flag As Boolean = False
'如果要装饰的对象不为空,就调用装饰方法
If Not (declogin Is Nothing) Then
flag = declogin.TestLogin(useren)
End If
Return flag
End Function
End Class ' TestUser
~~~
三个装饰类:
~~~
''' <summary>
''' 判断用户是否存在
''' </summary>
Public Class UserExist
Inherits BLL.TestUser
''' <summary>
''' 检测用户是否可以登陆_用户是否存在
''' </summary>
''' <param name="useren">用户类</param>
Public Overrides Function TestLogin(ByVal useren As UserEntity) As Boolean
'定义返回值
Dim flag As Boolean = False
Dim af As AbstractFactory.AbstractFactory = AbstractFactory.AbstractFactory.GetInstance()
Dim iuser As IDAL.IUser
'创建接口库
iuser = af.CreateUser()
If Not (iuser.QueryUserInfo(useren) Is Nothing) Then
flag = True
Else
Throw New Exception("用户名不存在,请检查用户名,重新登陆。")
End If
Return flag
End Function
End Class ' UserExist
~~~
~~~
''' <summary>
''' 验证密码是否正确
''' </summary>
Public Class TestPwd
Inherits BLL.TestUser
''' <summary>
''' 检测用户是否可以登陆_检测密码
''' </summary>
''' <param name="useren">用户类</param>
Public Overrides Function TestLogin(ByVal useren As UserEntity) As Boolean
'定义返回值
Dim flag As Boolean = False
Dim af As AbstractFactory.AbstractFactory = AbstractFactory.AbstractFactory.GetInstance()
Dim iuser As IDAL.IUser
'创建接口库
iuser = af.CreateUser()
If iuser.QueryUserInfo(useren).P_password = useren.P_password Then
flag = True
Else
Throw New Exception("登陆密码不正确,请重新输入。")
End If
Return flag
End Function
End Class ' TestPwd
~~~
~~~
''' <summary>
''' 查询用户状态
''' </summary>
Public Class UserState
Inherits TestUser
''' <summary>
''' 检测用户是否可以登陆——查询用户状态
''' </summary>
''' <param name="useren">用户类</param>
Public Overrides Function TestLogin(ByVal useren As UserEntity) As Boolean
'定义返回值
Dim flag As Boolean = False
Dim af As AbstractFactory.AbstractFactory = AbstractFactory.AbstractFactory.GetInstance()
Dim iuser As IDAL.IUser
'创建接口库
iuser = af.CreateUser()
If iuser.QueryUserInfo(useren).P_userstatus = "离线" Then
flag = True
Else
Throw New Exception("用户已经在线,不可以重复登陆。")
End If
Return flag
End Function
End Class ' UserState
~~~
主要的类都写完了,接下来就是客户端的调用了,
在调用是,根据装饰模式的特性,我们需要循环嵌套调用,为了使装饰的东西先后顺序稳定,
~~~
''' <summary>
''' 用户登陆系统方法
''' </summary>
''' <param name="useren">要登陆的用户实体</param>
Public Function Login(ByVal useren As UserEntity, worklogen As WorkLogEntity) As Boolean
Dim flag As Boolean = False
'判断用户是否存在
Dim Bqueryuser As New BLL.QueryUser
'定义登陆变量,存放用户登陆的几种情况
'Dim intLogin As Int32
'intLogin = Bqueryuser.UserInfo(useren)
'判断登陆的情况
'**************?????考虑使用迭代器模式
'使用装饰模式
'定义B层装饰模式对象
Dim Blogin As BLL.Login
'装饰对象声明
Dim BuserState As New BLL.UserState()
Dim BtestPWD As New BLL.TestPwd()
Dim BuserExist As New BLL.UserExist()
'Dim BtestUser As BLL.TestUser
'装饰过程
'4/声明验证对象
BuserState.SetObject(Blogin)
'3状态验证
BtestPWD.SetObject(BuserState)
'2密码验证
BuserExist.SetObject(BtestPWD)
'1验证用户是否存在
'验证全部通过,然后修改用户状态和插入工作记录
If BuserExist.TestLogin(useren) Then
'调用B层方法
If Buser.UpdateUserState(useren) Then
'声明工作记录对象
Dim Bworklog As New BLL.WorkLog
'调用B层方法
If Bworklog.AddWorkLog(worklogen) Then
'插入记录成功返回真
flag = True
Throw New Exception("用户登陆成功,马上跳转到主界面!")
End If
End If
End If
Return flag
End Function
~~~
通过以上的叙述,装饰模式就结束了。
如果用户需求发生变化,你需要增加价差,或者减少检查,只需要增加/去掉相应类即可,然后再客户端中修改调用对象即可。
看观察者怎么全方位观察机房收费系统
最后更新于:2022-04-01 09:30:34
## 一、观察者模式定义
观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题。这个主题对象在状态发生变化时,会通知所有的观察者对象,使他们能够自动更新自己。
解析:为什么会有观察者模式?
这里需要注意几点:
1、**一对多的依赖关系;**并不是说一对一不可用,只是一对一用观察者模式,没有必要。
2、**观察者接收到主题的通知后,自动更新状态:**观察者本身都有自己更新自己的方法,需要通知者这个触发者来触发,即观察者在主题达到某个条件后,开始自动更新。
## 二、看类图,找要点
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-18_56c5ce72e45a2.png)
从类图中可以看到图中有四个类:一个抽象通知者,一个具体通知者,一个抽象观察者,一个具体观察者。
具体观察者用来监听主题变化,注意图中没有主题类,有的是通知者,也就是说,主题通过通知者来通知观察者变化。
**1、观察者**
一对多的依赖关系就是指主题跟观察者之间的关系,继承自Observer的ConcreteObserver可以有多个。
SO,具体观察者共有的特点就是:Update方法的参数类型相同,返回值相同。只有这样的观察者才可以使用观察者模式。
**2、通知者**
具体通知者ConcreteSubject是通过实现一个接口Subject来实现通知任务的。既然有接口,很明显,可以有多个通知者,不同的通知者有各自对应的主题。
**3、观察者模式中的逻辑判断**——通知者方法(attach:增加观察者;detach:移除观察者)
方法中有增加,又有移除,意味这什么?
我们可以在一个主题中通知中,实现多次**逻辑判断**执行操作。例如:机房收费系统中的上机操作:在插入上机记录之前,需要通知三个观察者:卡号是否存在;卡内余额是否充足;查询卡状态;好了,接下来的逻辑判断是:如果前边三个条件满足,则通知以下这两个观察者:插入上机记录,修改卡状态。有了Attach 和Detach 这两个方法后,我们可以先添加前三个观察者,执行完后,判断结果,然后用Detach 方法把之前的观察者去掉,再Attach 新的观察者。去执行各自的操作。
## 三、实践
以机房收费系统为例,简单列出几个适合用到观察者模式的有:上机,下机,充值,注册,结账前的统计工作。
用图形化的语言表示如下:(图中没有结账)
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-18_56c5ce74ac6a3.png)
**既然可以有逻辑判断,观察者还可以同时调用观察者自己的多个方法。**
看结账观察者:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-18_56c5ce751cb9a.png)
图中没有抽象通知者,我自己给去掉了。为什么有抽象观察者,是为了降低耦合度才有的,而且像第一种情况那样,抽象观察者必须得有,但是结账这个观察者模式,我单抽出来,把它的更新方法做成两个,在系统中,这样的情况,貌似只有一种,所以我觉得把抽象通知者去掉,反而更简洁。
PS :这个结账的主题和观察者也可以合并到第一种情况中,在这里我只是为了说明
**1、观察者可以有多个更新自己的方法;**
**2、抽象观察者,在小的系统中,或者只有一个主题的通知者中可以省去;**
而特意拿出来的。
结语:这些只是个人见解,欢迎指正。
策略+简单工厂 实战篇
最后更新于:2022-04-01 09:30:31
机房收费系统收费制度——策略+简单工厂模式
前段时间把设计模式学了个遍,确切的说是初识。因为看过后,脑袋里就只剩下这几个模式叫什么了,怎么用,类图是什么,都不记得了。
现在机房收费系统重构,刚好拿几个过来练练手。
## 一、策略模式的使用环境
机房收费系统在下机收费这块,涉及到两种卡的收费,一种是固定用户,一种是临时用户。不同的用户消费单价不相同,导致在读取数据库的时候会有两种不同的查询方法,这样的情况适合使用策略模式来解决。(其实这里的情况比较简单,系统小,用模式有点复杂,这里只是为了学习策略模式和简单工厂)
## 二、具体实现
Client 类
~~~
'计算收费类
'相当于策略模式中的Client
'定义收费计算公式
Public Class CalculateConsume
'声明实体类——数据表
Dim dataen As New Model.Data_Table
'声明实体类对象——消费表
Dim customeren As New Model.Consume_Table
'定义dataaccess 类
Dim da As New DataAccess.DataAccess
'定义收费频率
Dim frequency As Integer
'定义准备时间
Dim timeready As Integer
'定义最少上机时间
Dim timemin As Integer
'计算公式
'调用ContextConsume类
Public Function CaculateMoney(cardnum As Integer) As Model.Consume_Table
'初始化策略模式配置类
Dim conprice As New ContextConsume(cardnum)
'调用方法,获取消费单价
Dim unitprice As Integer = conprice.Getresult()
'定义消费金额
Dim costmoney As Integer
'计算消费金额=单价*消费时长
costmoney = unitprice * Interval(GetOnTime(cardnum))
'把计算结果传值给实体类
customeren.P_cost = costmoney
customeren.P_interval = Interval(GetOnTime(cardnum))
'将(下机)现在时间传给实体类
customeren.P_offtime = Format(Now, "hh-nn")
Return customeren
End Function
'获取某个卡的上机时间——被Client 调用
Public Function GetOnTime(cardnnum) As Model.Consume_Table
'创建接口
Dim ico As IDAL.IConsume = da.CreateConsume()
'调用D层方法,并返回
Return ico.GetPOnTime(cardnnum)
End Function
'计算消费时长——把实体类当参数传递
Public Function Interval(customeren As Model.Consume_Table) As Long
'把读取来的上机时间赋值给日期变量
Dim ondate As Date = Format(CDate(customeren.P_ondate), "mm-dd")
Dim ontime As Date = Format(CDate(customeren.P_ontime), "HH-nn")
'以当前时间为结束时间
Dim enddate As Date = Format(Now, "mm-dd")
Dim endtime As Date = Format(Now, "HH-nn")
'调用系统函数 datediff 计算时间间隔
Dim costdate As Long = DateDiff("n", Trim(ondate), Trim(enddate))
Dim costtime As Long = DateDiff("n", Trim(ontime), Trim(endtime))
'调用系统函数cint 四舍五入来求的 时间间隔
'上机时间小于最小上机时间,按30分钟算
If (costdate + costtime) < timemin & (costdate + costtime) > timeready Then
Interval = 1
Else
'上机时间大于最小上机时间
Interval = CInt((costtime + costdate - timeready) / frequency)
End If
End Function
'取得基本数据表中的数据
Public Function GetData() As Model.Data_Table
'创建接口
Dim id As IDAL.IData = da.CreateData()
'调用D层方法——取得数据
dataen = id.GetData()
'取出收费频率
frequency = dataen.P_frequency
'取出最少上机时间
timemin = dataen.P_timemin
'取出准备时间
timeready = dataen.P_timeready
'返回值
Return dataen
End Function
End Class
~~~
策略类中接口
~~~
'策略模式
'根据用户类型,到数据库中去查询不同的消费价格
'不同的用户,实现查询的语句不同
Public Interface ConsumePrice
'读取不同用户的消费价格
Function Getprice() As Integer
End Interface
~~~
配置策略类的context +简单工厂实现
~~~
'关联策略模式
'实例化策略模式接口
Public Class ContextConsume
'声明消费单价类对象——策略模式
Dim cp As IStrategy.ConsumePrice
'构造函数,运用简单工厂,根据传入参数确定具体的收费策略
Public Sub New(cardnum As Integer)
'根据参数选择实例化对象
Select Case Cardtype(cardnum)
Case "临时用户"
cp = New TempConsume()
Exit Sub
Case "固定用户"
cp = New RegularConsume()
Exit Sub
End Select
End Sub
'根据卡类型,获取收费单价
Public Function Getresult() As Integer
'返回具体方法的实现
Return cp.Getprice()
End Function
'获取卡的类型
Public Function Cardtype(cardnum As Integer) As String
'声明dataaccess 类
Dim da As New DataAccess.DataAccess
'创建接口
Dim ic As IDAL.ICards = da.CreateCards()
'返回值
Return ic.GetCardType(cardnum)
End Function
End Class
~~~
实现接口的具体类
~~~
'获取固定用户的消费单价
Public Class RegularConsume
Implements IStrategy.ConsumePrice
'获取固定用户的消费单价
Public Function Getprice() As Integer Implements IStrategy.ConsumePrice.Getprice
'声明工厂对象
Dim da As New DataAccess.DataAccess
'创建接口
Dim id As IDAL.IData = da.CreateData
'创建datatable对象
Dim dataen As New Model.Data_Table
'调用D层方法实现
dataen = id.GetPriceRegular()
'取出需要的值,并返回
Return dataen.P_priceregular
End Function
End Class
~~~
临时用户
~~~
'获取临时用户的消费价格
Public Class TempConsume
'实现IStrategy接口
Implements IStrategy.ConsumePrice
'获取临时用户的消费价格
Public Function Getprice() As Integer Implements IStrategy.ConsumePrice.Getprice
'声明工厂对象
Dim da As New DataAccess.DataAccess
'创建接口
Dim id As IDAL.IData = da.CreateData
'创建datatable对象
Dim dataen As New Model.Data_Table
'调用D层方法实现
dataen = id.GetPriceTemp()
'取出需要的值,并返回
Return dataen.P_pricetemp
End Function
End Class
~~~
中介模式——行为模式之六
最后更新于:2022-04-01 09:30:29
## 零、问题
面向对象强调的是尽量把系统分割成许多对象,这样通常可以增加其复用性。但是如果有一个系统中有大量的对象,且彼此都需要联系,这样由于对象比较多,所以联系就会激增,大量的连接时的一个对象不可能在没有其他对象的支持下工作,系统表现为一个不可分割的整体,所以对系统的行为进行任何较大的改动就会变得困难,同时,代码的复用性也降低了。这样该怎么办呢?
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-18_56c5ce736517b.png)
## 一、中介者模式
为了解决上述问题,引出来中介者模式。
在将中介者模式之前,有一个法则必须先知道:迪米特法则——如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用。如果其中一个类需要调用另一个类的某一个方法的话,可以通过第三者转发这个调用。
中介者模式就是跟据迪米特法则进行设计的模式。
中介者模式:用一个中介对象封装一系列的对象交互。中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变他们之间的交互。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-18_56c5ce73753bb.png)
没有中介者模式时,各个对象之间彼此交错联系,相互织成了网状,通过中介者模式这个网状结构变成了以中介为中心的星形结构,每个对象不必通过直接联系与另一个对象发生相互作用,而是通过中介者对象与另一个对象发生相互作用,使得系统结构不会因为新对象的引入造成大量的修改工作。
## 二、类图
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-18_56c5ce73873cb.png)
职责模式——行为设计模式之四
最后更新于:2022-04-01 09:30:27
## 一、职责链模式
使对个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系.将这个对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。
客户端发出的请求时,并不知道哪一个对象最终处理这个请求,请求只是沿链传递,直至有一个对象负责处理它,且链中的对象自己也并不知道链的结构。这样系统的更改可以在不影响客户端的情况下动态的重新组织和分配责任,简化了对象的相互连接,他们仅需保持一个指向其后继者的引用,而不需保持它所有的候选接受者的引用,这样就降低了耦合度。
### 链的所在
我在理解这个模式时,觉得它之所以叫做职责链,最关键的是“链”这个字眼。从结构上来看,看下面的类图,没有什么特殊,一个个Handler类,几个concreteHandler来继承,并实现其功能。那么这个链体现在哪了呢?
在Handler类中的SetSuccessor方法。这个方法是用来设定后继者的。就是通过这个方法,一个个对象被连在一起。
客户发出一个请求,我们只需要给出处理请求链的开始端口,首个对象调用自己的HandRequest方法,解决了就结束请求,解决不了的请求,通过设定后继者后传,直到请求被解决。请求就这样被传递了,这些都是自动发生的。
有个比喻不知道恰当不?老师不知道拿错了谁的书,随便找了这个班一个人,把书给他负责把书还给书主人,这个同学,到班里,把书按座位传递,传到书主人那,就停止传递。按座位传递就是我们设定的职责链,每个同学就是一个个对象,书主人是最终处理请求的人。
## 二、类图
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-18_56c5ce7340c93.png)
问题:从ConcreteHandler到Handler为什么是聚合关系?
状态模式——行为型设计模式之三
最后更新于:2022-04-01 09:30:24
## 一、命令
将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。
命令模式联系生活,结合面向对象思想,最起码应该有四个类,命令发出者,命令传递命令接收者,还有命令本身。
或许有的人会说,命令传递者有点多余,多余吗?
为什么要加命令传递者?命令传递者可不仅是用来传递命令哦!
先从生活中抽象出一个例子:饭店服务员就好是模式中的命令传递者。
试想,饭店里没有服务员会怎么样?
顾客要跟厨师直接下订单。不论是菜做好了还是某一样菜没有,厨师都得找到具体顾客,一个顾客可以,顾客多了怎么办?厨师记得清楚吗?恐怕他自己做菜都忙不过来,那还记得是谁点的菜!到这还没有完,顾客要修改订单,想换菜,厨师这下头大了,是哪桌的菜啊,哪桌要换啊,哪桌先来的啊,哪个桌的菜没有了呀……
饭店服务员的任务是什么?
1、整理顾客的订单——增加,删除,修改。由于顾客或者饭店原因可能有事需要对订单进行修改,删除,这都是服务员任务。
2、记录订单的顺序
3、把整理好的订单传给厨师。
这样,他只需要按照服务员给他的订单先后做菜就Ok了,其间有什么变化,也是服务员来管理订单。要是没有那样菜,他也只需通过服务员传递给顾客,及时调换。
看来命令模式中,命令传递者是必不可少了。
剩下命令发出者,接受者,都各司其职,系统就运转起来了。
## 二、类图
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-18_56c5ce732c485.png)
命令模式——行为设计模式之二
最后更新于:2022-04-01 09:30:22
## 一、命令
将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。
命令模式联系生活,结合面向对象思想,最起码应该有四个类,命令发出者,命令传递命令接收者,还有命令本身。
或许有的人会说,命令传递者有点多余,多余吗?
为什么要加命令传递者?命令传递者可不仅是用来传递命令哦!
先从生活中抽象出一个例子:饭店服务员就好是模式中的命令传递者。
试想,饭店里没有服务员会怎么样?
顾客要跟厨师直接下订单。不论是菜做好了还是某一样菜没有,厨师都得找到具体顾客,一个顾客可以,顾客多了怎么办?厨师记得清楚吗?恐怕他自己做菜都忙不过来,那还记得是谁点的菜!到这还没有完,顾客要修改订单,想换菜,厨师这下头大了,是哪桌的菜啊,哪桌要换啊,哪桌先来的啊,哪个桌的菜没有了呀……
饭店服务员的任务是什么?
1、整理顾客的订单——增加,删除,修改。由于顾客或者饭店原因可能有事需要对订单进行修改,删除,这都是服务员任务。
2、记录订单的顺序
3、把整理好的订单传给厨师。
这样,他只需要按照服务员给他的订单先后做菜就Ok了,其间有什么变化,也是服务员来管理订单。要是没有那样菜,他也只需通过服务员传递给顾客,及时调换。
看来命令模式中,命令传递者是必不可少了。
剩下命令发出者,接受者,都各司其职,系统就运转起来了。
## 二、类图
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-18_56c5ce731b651.png)
模板设计——行为设计模式之一
最后更新于:2022-04-01 09:30:20
## 一、模板方法
定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
特点:模板方法时通过吧不变的行为搬移到超类,去除子类中的重复代码来体现它的优势。它提供了一个很好的代码复用平台。
适用范围:当不变的和可变的行为在方法的子类实现中混合在一起的时候,不变的行为就会在子类中重复出现。这时候就可以考虑用模板方法吧这些行为搬移到单一的地方,这样就帮助子类摆脱重复的不变行为的纠缠了。
例子:以前老师给学生考试,没钱,没有印刷机,只好把题目写在黑板上,然后全班同学在下面抄写,有时候,老是不小心写错了,全班同学跟着都得修改,不光这些,有的同学近视,经常抄错,这就导致答案也错。学生是一边抄一一边做题,抄完了,也答完了。
后来学校有钱了,买了印刷机,老师刻好模板,订正后,在复印卷子,这样学生省去了抄题目的麻烦,同时大家都是一样的卷子,不会再有抄错的情况。学生只需要把自己认为对的答案填上,交卷子即可。
这个考试分布进行的,首先是老师来刻模板,印卷子,把填写答案这步留给学生来实现。
模板方法有木有?
## 二、类图
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-18_56c5ce7307453.png)
在模板方法中,一般是父类中TemplateMethod方法中包含抽象方法PrimitiveOperation,而PrimitiveOperation方法在子类中通过多态来实现,这就完成了对算法局部的重定义。
观察者模式——行为型设计模式之五
最后更新于:2022-04-01 09:30:17
## 一、观察者模式
这个模式还有另一个名字发布——订阅模式。我觉得这个模式跟适合点。
定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。
这个模式跟我们的博客委员会的职能很相像哦!
每个月我们都要对博客进行一次检查。当管理员要结果时,博客委员会组长就得把各自组的博客结果发给管理员。
看一下我们的博客委员会的成立过程:
开始:当每个月底时,管理员会像考勤人员要统计结果。
那管理员怎么通知他们呢?
一个一个通知他们,让他们把结果发给管理员。这样,五个人就需要通知五遍,N个人就要通知N遍吗?显然不行。
建立一个飞信群——博客管理,把相关人员加入,直接在群里发一个通知,被加入群的人就可以收到通知,然后把整理结果发送。
接着:由于各种原因,现在管理人员需要2名,又增加一名监督人员,负责对考勤人员监督;照葫芦画瓢,我们可以让新增的管理人员模仿老管理人员,也建个群,也把这些人加进去;同时,还需要在群里增加监督人员。到月底时,管理员发送通知,考勤组和监督组分别执各自任务。
这样做目的达到!就是有点小问题,管理员得交给新管理员怎么做。假如急需要结果,管理员不在,怎么办,这样是不是很不灵活;要是再多一个小组呢?这个小组在接收到群消息时,应该做什么。这时候我们就需要考虑建立一个抽象模板,让每一个新加的管理员都按照模板的章程来办事,就不需要向非得等管理员了;给各个小组建立一个头目,让这个头目来对各个小组进行管理,只要把结果发给管理员即可,这就是博客委员会了。
怎么样?是不是发现,原来我们一直都在用观察者模式办事呢?只是不知道它还有一个这样的学名。
通知,接收,为了让各个对象耦合度降低,我们都抽象出一个类。
看一下类图,看看是不是这回事呢?
## 二、类图
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-18_56c5ce72e45a2.png)
代理模式——结构型设计模式之六
最后更新于:2022-04-01 09:30:15
## 一、代理模式
为其他对象提供一种代理以控制对这个对象的访问。
例子是这么说的:
A同学追C 同学,可是A同学不敢直接送东西给C同学,于是就通过B同学把东西转交给C同学。B同学送给C同学的东西都是A同学的,也就是说B的所有动作A同学的本意,并非B的本意哦!最后,B同学和C同学在一起了,当然这是故事情节,不管他了。这里的B同学就是我们要说的代理模式的代理。
**重点**:我们知道B送给C的东西和他发出的动作其实都是从A那拿来的,即代理只是引用真实实体的方法。
这个模式理解的稀里糊涂?请大家帮忙指点一下。
一个对象是不是可以有多个代理呢?
针对不同要访问的对象设置不同的代理。
## 二、类图
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-18_56c5ce72d1924.png)
## 三、代理模式的应用
1、远程代理:为一个对象在不同的地址空间提供局部代表。这样可以隐藏一个对象存在于不同地址空间的事实。
2、虚拟代理:根据需要创建开销很大的对象。通过它来存放实例化需要很长时间的真实对象。
3、安全代理:用来控制真实对象访问时的权限。
4、智能指引:是指当调用真实的对象时,代理处理另外一些事。
外观模式——结构型设计模式之五
最后更新于:2022-04-01 09:30:13
## 一、外观模式
为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
**为什么使用外观模式?**
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-18_56c5ce72a03a7.png)
当股票一,二,三,国债一,二任一个发生变化时,都会直接影响到每一个客户。这样耦合度太高。
我们或一种方式来看:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-18_56c5ce72af3af.png)
当股票一,二,三,国债一,二任一个发生变化时,只会直接影响到基金,而客户不用关心这些股票和国债的变化,他们只知道基金的增益对他们有影响。因为基金是由很多这些股票和国债聚合成,所以不会因为一两个变化,而产生大的波动,相对比较稳定。
所以选择后一种方式,可以降低耦合度。减少对象之间的关联性,以维护和管理。
## 二、类图
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-18_56c5ce72bed43.png)
Client跟Façade联系,Facade来管理子系统。
装饰模式——结构型设计模式之四
最后更新于:2022-04-01 09:30:11
## 一、装饰模式
动态地给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更为灵活。
**装饰模式的出现**
用例子说明:我们想要表达一个人,穿不同衣服,体现不同个性的一个程序。
首先定义一个人Person类;在定义一个抽象的衣服Clothes类,然后有各种衣服的子类。
在客户端通过实例化“人”,然后再实例化需要的“衣服”,分别调用show方法。
这样也可以达到目的,但是我们看分析一下客户端。
~~~
Person a=new Person("小明");
T恤 t=new T恤();
垮裤 K=new 垮裤();
球鞋 q=new 球鞋();
//显示
a.show();
t.show();
k.show();
q.show();
~~~
**问题一:**从上述代码中可以看出这些穿衣服的顺序我们可以任意调换,但是一调换,是不是,可能就是另外一种装扮了,所以这样写的代码结构不稳定。
**问题二:**以上是一种装扮,如果我要展示N中装扮呢,我就需要把这些代码重复N遍。
**为了避免这些问题,所以有了装饰模式。**
使用装饰模式可以再客户端中将装饰过程进行嵌套封装,即使同样的衣服,改变一下顺序,就可以穿出两种风格。而且,穿衣服的件数不一定相同。
同时还减少了代码的数量,只需要用一个show方法就可以调出嵌套的一系列动作。
**使用装饰模式:**
需要再衣服类中增加一个Decorate方法,用来给嵌套的要装饰对象赋值。
代码显示:
~~~
Person a=new Person("小明");
T恤 t=new T恤();
垮裤 K=new 垮裤();
球鞋 q=new 球鞋();
//动作嵌套
q.decorate(a);
k.decorate(q);
t.decorate(k);
//显示
t.show();
~~~
## 二、类图
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-18_56c5ce724b74f.png)
组合模式——结构型设计模式之三
最后更新于:2022-04-01 09:30:08
## 一、组合模式
将对象组合成树形结构以表示“部分——整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。
组合模式适用范围:树形结构的“部分——整体”的关系中。
## 二、类图
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-18_56c5ce71cc60b.png)
上图中:Component表示的不是整体,整体也好,部分也罢,Component表示的是他们共有的操作,定义成接口,所以从Component到Composite是聚合关系。
不同看法:
也正因为这样,所以我觉得Client应该和Composite有联系,而不是Component。毕竟Composite中定义的才是具体的物件,在Component中是一些操作。
我的理解是下面这幅图:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-18_56c5ce7221e87.png)
可以这么理解吗?
桥接模式——结构性设计模式之二
最后更新于:2022-04-01 09:30:06
## 一、桥接模式
将抽象部分与它的实现部分分离,使他们都可以独立的变化。
什么叫抽象与它的是实现分离呢?
其实这并不是说,让抽象类与其派生类分离,这样也没有意义;实现指的是抽象类和它的派生类用来实现自己的对象。
像手机品牌和手机软件的关系。我们可以按手机品牌来分类,
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-18_56c5ce7187a8c.png)
同时,还可以按照手机软件来给软件分类。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-18_56c5ce719a192.png)
现在我们要是想增加三个手机品牌应该怎么办?
用第一幅图来说,我们需要在第二层增加三个类,再在第三层增加留个类;如果是第二种分法,需要再第三层加六个类。
或者说要增加三个软件,又该怎么办?
这时候的情况原理同上。
那我要是既增加手机软件,又增加手机品牌呢????
这样,两种分法,做出调整都是一个巨大的工程,关键是我们这样修改还违背了软件模式设计原则:开发封闭原则。
不管我们是增加手机,还是手机软件,都会影响到彼此,所以为了解决这种情况,我们采用了一种设计模式——桥接模式。
在说桥接模式前,先说一个原则:合成/聚合复用原则。
**合成/**聚合复用原则**:尽量使用合成/聚合,不要使用继承。优先使用对象的合成/聚合将有助于你保持每个类被封装,并被集中在单个任务上,这样类和类继承层次会保持较小规模,并且不太可能增长为不可控制的庞然大物。
遇到类似这种多种分类方式的情况下,我们不要一味的依赖继承,这样会继承越多,我们就把多种分类方式分别写出来,然后根据“合成/聚合复用”的原则对父类进行连接。这样,在修改各自类的时候不会干扰的彼此,各自的类都有了良好的封装和扩展。
**结构图如下:**
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-18_56c5ce71aa725.png)
## 二、类图如下:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-18_56c5ce71bbd80.png)
注意:类图中的Abstraction是一个抽象的抽象类,Implementor是具体的实现类。桥接模式运用合成/聚合复用的原则把抽象和实现分开来表示,而不是使用继承,把层次累积成庞然大物。
适配器 and 组合模式——结构性模式之一
最后更新于:2022-04-01 09:30:04
## 适配器模式
## 一、适配器模式
讲一个类的接口装换成客户希望的另外一个接口适配器模式使得原本由于接口不兼容而不能一起工作的哪些类可以一起工作。
用于解决什么问题?
系统的数据和行为都正确,但接口不符时,我们应该考虑用适配器,目的是使控制范围之外的一个原有对象与某个接口匹配。适配器模式主要用于希望复用一些现存的类,但是接口又与复用环境要求不一致的情况下。
简单的说:我们需要的东西就在面前,可是就是不能使用,又不可以改造他,那么我们就使用适配器模式。
现实生活总的适配器模式很多:电源适配器,只要有电,不管多少伏,使用电源适配器都可以把它变成需要的电压;翻译员,A国家的人听不懂B国家的人的语言,通过一个翻译,就可以明白。这些都是适配器的工作。
## 二、类图
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-18_56c5ce7103430.png)
## 三、注意
Adapter与Target是继承关系。
在Adapter的Request方法中调用Adaptee中SpecificRequest方法来实现翻译的目的。
我们在客户端只需要实例化一个Target类型的翻译即可调用Adaptee类。
## 组合模式
## 一、组合模式
将对象组合成树形结构以表示“部分——整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。
组合模式适用范围:树形结构的“部分——整体”的关系中。
## 二、类图
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-18_56c5ce7115b19.png)
上图中:Component表示的不是整体,整体也好,部分也罢,Component表示的是他们共有的操作,定义成接口,所以从Component到Composite是聚合关系。
不同看法:
也正因为这样,所以我觉得Client应该和Composite有联系,而不是Component。毕竟Composite中定义的才是具体的物件,在Component中是一些操作。
我的理解是下面这幅图:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-18_56c5ce715f3f9.png)
可以这么理解吗?
原型模式——创建型设计模式四
最后更新于:2022-04-01 09:30:01
## 一、原型模式
用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
**用实例来说话:我要发简历,自己写了一份模板,然后复制了N 份。**如下:
~~~
//模板:class Resume
{
Private string name;
Public Resume (string name)
{
This.name=name;
}
}
//客户端:
Static void Main(string[] args)
{
Resume a=new Resume("大鸟");
Resume b=new Resume("大鸟");
Resume b=new Resume("大鸟");
}
~~~
如果现在我想改,那就得把N份全改了,本来只是改动一点,却需要很长时间,没效率,真是费力不讨好的工作。
**原型模式可以改变这种低级费力的工作。**
原型模式其实就是从一个对象在创建灵位一个可定制的对象,而且不需知道任何创建的细节。在原型模式通过克隆Clone来实现对象之间的这种定制。
区别:以前的复制是每个对象都去实例化类,现在的克隆,是只实例化一个对象,然后通过这个对象传递给其他对象。
具体如下:
~~~
class Resume
{
Private string name;
Public Resume (string name)
{
This.name=name;
}
//模板中增加克隆方法
Public object Clone()
{
Return (Object)this.MemberwiseClone();
}
}
Static void Main(string[] args)
{
Resume a=new Resume("大鸟");
Resume b=(Resume)a.clone();
Resume c=(Resume)a.clone();
}
~~~
Clone方法时将当前对象的非静态字段复制到该新对象,如果字段是值类型,则对该字段执行逐位复制。如果字段是引用类型,则复制引用但不复制引用的对象。
有人有问了,那我要向实现将引用的对象也克隆过来怎么办?
上边的方法在模板中添加Clone 是一种浅复制,还有一种深复制,可以解决克隆引用对象的问题。
**深复制**
深复制需要增加三步:
1、让引用的类也继承系统的克隆类Icloneable,在类中添加克隆方法。
2、在调用类中,增加以被调用类为参数的私用构造函数,结果返回被调用类克隆方法,从而实现克隆。
3、在调用类的克隆方法中,调用私用构造方法,让引用克隆完成。
假如工作经历类为要调用的类:
**1、让工作经历类继承接口**
~~~
Class WorkExperience :Icloneable
{
//增加克隆方法
Public Object Clone()
{
Return (Object) this.MemberwiseClone();
}
}
~~~
**2、在简历类中增加私有构造函数**
~~~
Private Resume(WorkExperience work)
{
This.work=(WorkExperience)work.Clone();
}
~~~
**3、修改简历类中的克隆方法。**
~~~
public Object Clone()
{
Resume obj=new Resume(this.work);
……
}
~~~
## 二、类图
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-18_56c5ce70e6a6d.png)
建造者模式——创建型模式之三
最后更新于:2022-04-01 09:29:59
## 一、建造者模式
将一个复杂的对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
### 建造模式的出现
**引子**
> 借用书上的例子,那炒菜(西红柿鸡蛋)来说,我们需要的原材料有:鸡蛋,西红柿;基本调料:油,盐/糖,葱花。
> 每次我们都需要重复这样的操作,可悲的是,尽管调料,材料都很少,厨师还是会出现忘记加盐,或者葱花的情况。怎么办?
> 为了解决这个问题,我们来分析一下,这些东西都是做菜不可缺少的东西,我们可以给这个菜制定一个流程,每次都按照这个流程去做,这样就不会忘记。
> 当然有的人胃口比较重,需要多加盐,有的人喜欢吃甜的,那我们就需要加一样调料:糖。针对这些具体的细节要求,我们需要有在做菜前有一个说明,告诉厨师多加盐,或者放糖,不放盐。
把这个模式放到编程的世界就是建造者模式。
来类比一下:
<table border="1" cellspacing="0" cellpadding="0" valign="top" style="border-bottom:#a3a3a3 1pt solid; border-left:#a3a3a3 1pt solid; border-collapse:collapse; direction:ltr; margin-left:0.333in; border-top:#a3a3a3 1pt solid; border-right:#a3a3a3 1pt solid"><tbody><tr><td style="border-bottom:#a3a3a3 1pt solid; border-left:#a3a3a3 1pt solid; padding-bottom:4pt; padding-left:4pt; width:1.202in; padding-right:4pt; vertical-align:top; border-top:#a3a3a3 1pt solid; border-right:#a3a3a3 1pt solid; padding-top:4pt"><p style="margin:0in; font-family:SimSun; font-size:14pt">生活</p></td><td style="border-bottom:#a3a3a3 1pt solid; border-left:#a3a3a3 1pt solid; padding-bottom:4pt; padding-left:4pt; width:1.211in; padding-right:4pt; vertical-align:top; border-top:#a3a3a3 1pt solid; border-right:#a3a3a3 1pt solid; padding-top:4pt"><p style="margin:0in; font-family:SimSun; font-size:14pt">西红柿流程</p></td><td style="border-bottom:#a3a3a3 1pt solid; border-left:#a3a3a3 1pt solid; padding-bottom:4pt; padding-left:4pt; width:0.813in; padding-right:4pt; vertical-align:top; border-top:#a3a3a3 1pt solid; border-right:#a3a3a3 1pt solid; padding-top:4pt"><p style="margin:0in; font-family:SimSun; font-size:14pt">说明</p></td><td style="border-bottom:#a3a3a3 1pt solid; border-left:#a3a3a3 1pt solid; padding-bottom:4pt; padding-left:4pt; width:1.3in; padding-right:4pt; vertical-align:top; border-top:#a3a3a3 1pt solid; border-right:#a3a3a3 1pt solid; padding-top:4pt"><p style="margin:0in; font-family:SimSun; font-size:14pt">加盐西红柿</p></td><td style="border-bottom:#a3a3a3 1pt solid; border-left:#a3a3a3 1pt solid; padding-bottom:4pt; padding-left:4pt; width:1.34in; padding-right:4pt; vertical-align:top; border-top:#a3a3a3 1pt solid; border-right:#a3a3a3 1pt solid; padding-top:4pt"><p style="margin:0in; font-family:SimSun; font-size:14pt">加糖西红柿</p></td></tr><tr><td style="border-bottom:#a3a3a3 1pt solid; border-left:#a3a3a3 1pt solid; padding-bottom:4pt; padding-left:4pt; width:1.202in; padding-right:4pt; vertical-align:top; border-top:#a3a3a3 1pt solid; border-right:#a3a3a3 1pt solid; padding-top:4pt"><p style="margin:0in; font-family:SimSun; font-size:14pt">建造者模式</p></td><td style="border-bottom:#a3a3a3 1pt solid; border-left:#a3a3a3 1pt solid; padding-bottom:4pt; padding-left:4pt; width:1.211in; padding-right:4pt; vertical-align:top; border-top:#a3a3a3 1pt solid; border-right:#a3a3a3 1pt solid; padding-top:4pt"><p style="margin:0in; font-family:SimSun; font-size:14pt">建造者</p></td><td style="border-bottom:#a3a3a3 1pt solid; border-left:#a3a3a3 1pt solid; padding-bottom:4pt; padding-left:4pt; width:0.813in; padding-right:4pt; vertical-align:top; border-top:#a3a3a3 1pt solid; border-right:#a3a3a3 1pt solid; padding-top:4pt"><p style="margin:0in; font-family:SimSun; font-size:14pt">指挥者</p></td><td style="border-bottom:#a3a3a3 1pt solid; border-left:#a3a3a3 1pt solid; padding-bottom:4pt; padding-left:4pt; width:1.3in; padding-right:4pt; vertical-align:top; border-top:#a3a3a3 1pt solid; border-right:#a3a3a3 1pt solid; padding-top:4pt"><p style="margin:0in; font-size:14pt"><span lang="zh-CN" style="font-family:SimSun">建造者子类</span><span lang="en-US" style="font-family:Calibri">1</span></p></td><td style="border-bottom:#a3a3a3 1pt solid; border-left:#a3a3a3 1pt solid; padding-bottom:4pt; padding-left:4pt; width:1.34in; padding-right:4pt; vertical-align:top; border-top:#a3a3a3 1pt solid; border-right:#a3a3a3 1pt solid; padding-top:4pt"><p style="margin:0in; font-size:14pt"><span lang="zh-CN" style="font-family:SimSun">建造者子类</span><span lang="en-US" style="font-family:Calibri">2</span></p></td></tr></tbody></table>
建造者模式把一个对象的基本构建给造出来,然后根据修饰细节的不同再进行扩展创建。
注意:
1、所有的扩展子类都必须实现父类——保证建造的初衷不变。
2、子类的扩展时对父类的细节修饰——不是实质改变
## 二、类图
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-18_56c5ce70d2b5e.png)
从图中可以看出,具体的实现是CreateeBuilder来完成的,而CreateBulider之间具体的不同又是依赖现实生活中的东西Things来体现的。