本篇是面向对象设计系列文章的第四篇,讲解的是设计模式中的7个比较常见的行为型模式(按照本文讲解顺序排列):java
在模板模式(Template Method Pattern)中,定义一个操做中的算法的框架,而将一些步骤的执行延迟到子类中,使得子类能够在不改变算法的结构的前提下便可从新定义该算法的某些特定步骤。node
一般一个算法须要几个执行步骤来实现,而有时咱们须要定义几种执行步骤一致,可是却可能在某个步骤的实现略有差别的算法。也就是说咱们既须要复用实现相同的步骤,也能够经过在某个步骤的不一样实现来灵活扩展出更多不一样的算法。git
在这种场景下,咱们可使用模板方法模式:定义好一个算法的框架,在父类实现能够复用的算法步骤,而将须要扩展和修改其余步骤的任务推迟给子类进行。程序员
如今咱们清楚了模板方法模式的适用场景,下面看一下这个模式的成员和类图。github
模板方法模式的成员除了客户端之外,只有两个成员:算法
有些参考资料定义这两个成员为
Abstract Class
和Concrete Class
。编程
下面经过类图来看一下命令模式各个成员之间的关系:设计模式
由上图能够看出,Algorithm
的excute
方法是算法接口,它在内部调用了三个步骤方法:step1
,step2
,step3
。而step2
是未暴露在外部的,由于这个步骤是须要各个子类复用的。所以Algorithm
只将step1
和step3
暴露了出来以供子类来调用。数组
模拟一个制做三种热饮的场景:热美式咖啡,热拿铁,热茶。安全
这三种热饮的制做步骤是一致的,都是三个步骤:
虽然制做步骤是一致的,可是不一样种类的热饮在每一步多是不一样的:咖啡和茶叶主成分是咖啡粉和茶叶;而辅助成分:美式咖啡和茶叶能够不添加,而拿铁还需添加牛奶。
而第一步是相同的:准备热水。
根据上面对模板方法模式的介绍,像这样算法步骤相同,算法步骤里的实现可能相同或不一样的场景咱们可使用模板方法模式。下面咱们看一下如何用代码来模拟该场景。
首先咱们建立算法类HotDrink
:
//================== HotDrink.h ==================
@interface HotDrink : NSObject
- (void)makingProcess;
- (void)addMainMaterial;
- (void)addIngredients;
@end
//================== HotDrink.m ==================
@implementation HotDrink
- (void)makingProcess{
NSLog(@" ===== Begin to making %@ ===== ", NSStringFromClass([self class]));
[self boilWater];
[self addMainMaterial];
[self addIngredients];
}
- (void)prepareHotWater{
NSLog(@"prepare hot water");
}
- (void)addMainMaterial{
NSLog(@"implemetation by subClasses");
}
- (void)addIngredients{
NSLog(@"implemetation by subClasses");
}
@end
复制代码
HotDrink
向外部暴露了一个制做过程的接口makingProcess
,这个接口内部调用了热饮的全部制做步骤方法:
- (void)makingProcess{
//准备热水
[self prepareHotWater];
//添加主成分
[self addMainMaterial];
//添加辅助成分
[self addIngredients];
}
复制代码
HotDrink
只向外暴露了这三个步骤中的两个须要子类按照本身方式实现的接口:
//添加主成分
- (void)addMainMaterial;
//添加辅助成分
- (void)addIngredients;
复制代码
由于热饮的第一步都是一致的(准备热水),因此第一步骤的接口没有暴露出来给子类实现,而是直接在当前类实现了,这也就是模板方法的一个能够复用代码的优势。
OK,咱们如今建立好了算法类,那么根据上面的需求,咱们接着建立三个具体算法类:
HotDrinkTea
: 热茶HotDrinkLatte
: 热拿铁HotDrinkAmericano
: 热美式//================== HotDrinkTea.h ==================
@interface HotDrinkTea : HotDrink
@end
//================== HotDrinkTea.m ==================
@implementation HotDrinkTea
- (void)addMainMaterial{
NSLog(@"add tea leaf");
}
- (void)addIngredients{
NSLog(@"add nothing");
}
@end
复制代码
热茶在addMainMaterial
步骤里面是添加了茶叶,而在addIngredients
步骤没有作任何事情(这里先假定是纯的茶叶)。
相似地,咱们看一下两种热咖啡的实现。首先是热拿铁HotDrinkLatte
:
//================== HotDrinkLatte.h ==================
@interface HotDrinkLatte : HotDrink
@end
//================== HotDrinkLatte.m ==================
@implementation HotDrinkLatte
- (void)addMainMaterial{
NSLog(@"add ground coffee");
}
- (void)addIngredients{
NSLog(@"add milk");
}
@end
复制代码
热拿铁在addMainMaterial
步骤里面是添加了咖啡粉,而在addIngredients
步骤添加了牛奶。
下面再看一下热美式HotDrinkAmericano
:
//================== HotDrinkAmericano.h ==================
@interface HotDrinkAmericano : HotDrink
@end
//================== HotDrinkAmericano.m ==================
@implementation HotDrinkAmericano
- (void)addMainMaterial{
NSLog(@"add ground coffee");
}
- (void)addIngredients{
NSLog(@"add nothing");
}
@end
复制代码
热美式在addMainMaterial
步骤里面是添加了咖啡粉,而在addIngredients
步骤没有作任何事,由于美式就是纯的咖啡,理论上除了水和咖啡不须要添加任何其余东西。
到如今三种热饮类建立好了,咱们如今分别制做这三种热饮,并看一下日至输出:
===== Begin to making HotDrinkTea =====
prepare hot water
add tea leaf
add nothing
===== Begin to making HotDrinkLatte =====
prepare hot water
add ground coffee
add milk
===== Begin to making HotDrinkAmericano =====
prepare hot water
add ground coffee
add nothing
复制代码
上面的日至输出准确无误地反映了咱们所定义的这三种热饮制做过程:
下面看一下上面代码对应的类图。
UIView
的drawRect:
方法能够自定义绘图,是模板方法模式的一种实践。java.lang.Runnable
是使用JDK的经典场景:Runnable
接口能够做为抽象的命令,而实现了Runnable的线程便是具体的命令。策略模式(Strategy Pattern):定义一系列算法,将每个算法封装起来,并让它们能够相互替换。
有时候在实现某一个功能的时可能会有多个方案:咱们须要让系统能够动态灵活地更换方案;并且也可以让开发者方便地增长新的方案或删除旧的方案。
若是咱们将全部的方案硬编码在同一个类中,那么在从此修改,添加,删除某个方案的时候就会改动原有类,这是违反开闭原则的。
其实咱们能够定义一些独立的类来封装不一样的解决方案,每个类封装一个具体的方案,这些不一样的方案就是咱们所说的策略。并且咱们能够用一个抽象的策略类来保证这些策略的一致性,这就是策略模式的设计方案。
如今咱们清楚了策略模式的适用场景,下面看一下策略模式的成员和类图。
策略模式除了客户端以外共有三个成员:
下面咱们经过类图来看一下各个成员之间的关系。
模拟一个两个整数能够随意替换加减乘除算法的场景。
在该场景中,传入的两个整数参数是不变的,可是对于这两个整数的具体操做能够灵活切换,那么咱们可使用策略模式:将每一个操做(算法)封装起来,在须要替换的时候将Context
类持有的具体策略实例更新便可。
首先咱们定义好抽象策略类和具体策略类:
由于是针对两个整数的操做,因此在抽象策略类中,咱们只需定义一个传入两个整数的接口便可。
抽象策略类TwoIntOperation
:
//================== TwoIntOperation.h ==================
@interface TwoIntOperation : NSObject
- (int)operationOfInt1:(int)int1 int2:(int)int2;
@end
//================== TwoIntOperation.m ==================
@implementation TwoIntOperation
- (int)operationOfInt1:(int)int1 int2:(int)int2{
//implenting by sub classes;
return 0;
}
@end
复制代码
接着咱们根据加减乘除四种运算,来分别定义四个具体策略类:
加法TwoIntOperationAdd
:
//================== TwoIntOperationAdd.h ==================
@interface TwoIntOperationAdd : TwoIntOperation
@end
//================== TwoIntOperationAdd.m ==================
@implementation TwoIntOperationAdd
- (int)operationOfInt1:(int)int1 int2:(int)int2{
NSLog(@"==== adding ====");
return int1 + int2;
}
@end
复制代码
减法TwoIntOperationSubstract
:
//================== TwoIntOperationSubstract.h ==================
@interface TwoIntOperationSubstract : TwoIntOperation
@end
//================== TwoIntOperationSubstract.m ==================
@implementation TwoIntOperationSubstract
- (int)operationOfInt1:(int)int1 int2:(int)int2{
NSLog(@"==== Substract ====");
return int1 - int2;
}
@end
复制代码
乘法TwoIntOperationMultiply
:
//================== TwoIntOperationMultiply.h ==================
@interface TwoIntOperationMultiply : TwoIntOperation
@end
//================== TwoIntOperationMultiply.m ==================
@implementation TwoIntOperationMultiply
- (int)operationOfInt1:(int)int1 int2:(int)int2{
NSLog(@"==== multiply ====");
return int1 * int2;
}
@end
复制代码
除法TwoIntOperationDivision
:
//================== TwoIntOperationDivision.h ==================
@interface TwoIntOperationDivision : TwoIntOperation
@end
//================== TwoIntOperationDivision.m ==================
@implementation TwoIntOperationDivision
- (int)operationOfInt1:(int)int1 int2:(int)int2{
NSLog(@"==== division ====");
return int1/int2;
}
@end
复制代码
如今关于算法的类都声明好了,咱们最后声明一下 Context
类:
//================== Context.h ==================
@interface Context : NSObject
- (instancetype)initWithOperation: (TwoIntOperation *)operation;
- (void)setOperation:(TwoIntOperation *)operation;
- (int)excuteOperationOfInt1:(int)int1 int2:(int)int2;
@end
//================== Context.m ==================
@implementation Context
{
TwoIntOperation *_operation;
}
- (instancetype)initWithOperation: (TwoIntOperation *)operation{
self = [super init];
if (self) {
//injection from instane initialization
_operation = operation;
}
return self;
}
- (void)setOperation:(TwoIntOperation *)operation{
//injection from setting method
_operation = operation;
}
- (int)excuteOperationOfInt1:(int)int1 int2:(int)int2{
//return value by constract strategy instane
return [_operation operationOfInt1:int1 int2:int2];
}
@end
复制代码
Context
类在构造器(init方法)注入了一个具体策略实例并持有它,并且Context
也提供了set
方法,让外部注入进来具体策略类的实例。
而策略的具体执行是经过Context
的接口excuteOperationOfInt1:int2
。这个接口是提供给客户端调用的;并且在它的内部其实调用的是当前持有的策略实例的执行策略的方法。
因此若是想使用哪一种策略,只要将具体策略的实例传入到Context
实例便可。
如今全部的类都定义好了,下面咱们看一下具体如何使用:
int int1 = 6;
int int2 = 3;
NSLog(@"int1: %d int2: %d",int1,int2);
//Firstly, using add operation
TwoIntOperationAdd *addOperation = [[TwoIntOperationAdd alloc] init];
Context *ct = [[Context alloc] initWithOperation:addOperation];
int res1 = [ct excuteOperationOfInt1:int1 int2:int2];
NSLog(@"result of adding : %d",res1);
//Changing to multiple operation
TwoIntOperationMultiply *multiplyOperation = [[TwoIntOperationMultiply alloc] init];
[ct setOperation:multiplyOperation];
int res2 = [ct excuteOperationOfInt1:int1 int2:int2];
NSLog(@"result of multiplying : %d",res2);
//Changing to substraction operation
TwoIntOperationSubstract *subOperation = [[TwoIntOperationSubstract alloc] init];
[ct setOperation:subOperation];
int res3 = [ct excuteOperationOfInt1:int1 int2:int2];
NSLog(@"result of substracting : %d",res3);
//Changing to division operation
TwoIntOperationDivision *divisionOperation = [[TwoIntOperationDivision alloc] init];
[ct setOperation:divisionOperation];
int res4 = [ct excuteOperationOfInt1:int1 int2:int2];
NSLog(@"result of dividing : %d",res4);
复制代码
看一下日至输出:
[13431:1238320] int1: 6 int2: 3
[13431:1238320] ==== adding ====
[13431:1238320] result of adding : 9
[13431:1238320] ==== multiply ====
[13431:1238320] result of multiplying : 18
[13431:1238320] ==== Substract ====
[13431:1238320] result of substracting : 3
[13431:1238320] ==== division ====
[13431:1238320] result dividing : 2
复制代码
在上面的例子中,首先咱们要使用加法,因此 实例化了加法策略类并传入到了Context
类的构造器中。
然后续的乘法,减法,除法的更换,则是分别将它们的策略实例传入到了Context
的set方法中,并执行便可。
下面看一下上面代码对应的类图。
Comparator
是策略模式的实现,可使用不一样的子类,也就是具体策略来解决不一样的需求。责任链模式(Chain of Responsibility Pattern):为请求建立了一个接收者对象的链,每一个接收者都包含对另外一个接收者的引用。若是一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。
在处理某个请求的时候,解决策略因条件不一样而不一样。这时,相对于使用if-else
来区分不一样的条件和对应的解决策略,咱们可使用责任链模式,将不一样条件和对应的解决策略封装到一个类中,即不一样的处理者。而后将这些处理者组成责任链,在当前处理者没法处理或不符合当前条件时,将请求传递给下一个处理者。
如今咱们清楚了责任链模式的适用场景,下面看一下责任链模式的成员和类图。
责任链模式的结构比较简单,不包括客户端只有两个成员:
模拟一个 ATM 取现金的场景:ATM机器有50,20,10面值的纸币,根据用户须要提取的现金金额来输出纸币张数最少的等价金额的纸币。
好比用户须要取130元,则ATM须要输出2张50面额的纸币,1张20面额的纸币,1张10面额的纸币;而不是6张20面额的纸币加1张10面额的纸币。
显然,为了输出最少张数的纸币,ATM在计算的时候是从面额最大的纸币开始计算的。
若是不使用责任链模式,咱们可能会写一个do-while
循环,在循环里面再根据纸币的面额在作if-else
判断,不断去尝试直到将面额除尽(没有余数)。可是若是将来面额的数值发生变化,或者添加新的面额的纸币的话,咱们还须要更改判断条件或增长if-else
语句,这显然违反了开闭原则。
可是若是使用责任链模式,咱们将每一个面值的纸币当作责任链中的一个处理者(节点,node),自成一类,单独作处理。而后将这些处理者按照顺序链接起来(50,20,10),按照顺序对用户输入的数值进行处理便可。
这样作的好处是,若是之后修改面值或添加一种新的面值,咱们只须要修改其中某一个处理者或者新建一个处理者类,再从新插入到责任链的合适的位置便可。
下面咱们看一下如何用代码来模拟该场景。
首先建立抽象处理者DispenseChainNode
:
//================== DispenseChainNode.h ==================
@interface DispenseChainNode : NSObject <DispenseProtocol>
{
@protected DispenseChainNode *_nextChainUnit;
}
- (void)setNextChainUnit:(DispenseChainNode *)chainUnit;
@end
//================== DispenseChainNode.m ==================
@implementation DispenseChainNode
- (void)setNextChainNode:(DispenseChainNode *)chainNode{
_nextChainNode = chainNode;
}
- (void)dispense:(int)amount{
return;
}
@end
复制代码
DispenseChainNode
是责任链节点,也就是具体处理者的父类,它持有DispenseChainNode
的实例,用来保存当前节点的下一个节点。这个下一个节点的实例是经过setNextChainNode:
方法注入进来的 并且,DispenseChainNode
遵循<DispenseProtocol>
协议,这个协议只有一个方法,就是dispense:
方法,每一个节点都实现这个方法来对输入的金额作处理。(dispense 单词的意思是分配,分发)
如今咱们根据需求,建立具体处理者,也就是针对50,20,10面额的具体处理者:
50面额的具体处理者:
//================== DispenseChainNodeFor50Yuan.h ==================
@interface DispenseChainNodeFor50Yuan : DispenseChainNode
@end
//================== DispenseChainNodeFor50Yuan.m ==================
@implementation DispenseChainNodeFor50Yuan
- (void)dispense:(int)amount{
int unit = 50;
if (amount >= unit) {
int count = amount/unit;
int remainder = amount % unit;
NSLog(@"Dispensing %d of %d",count,unit);
if (remainder != 0) {
[_nextChainNode dispense:remainder];
}
}else{
[_nextChainNode dispense:amount];
}
}
@end
复制代码
20面额的具体处理者:
//================== DispenseChainNodeFor20Yuan.h ==================
@interface DispenseChainNodeFor20Yuan : DispenseChainNode
@end
//================== DispenseChainNodeFor20Yuan.m ==================
@implementation DispenseChainNodeFor20Yuan
- (void)dispense:(int)amount{
int unit = 20;
if (amount >= unit) {
int count = amount/unit;
int remainder = amount % unit;
NSLog(@"Dispensing %d of %d",count,unit);
if (remainder != 0) {
[_nextChainNode dispense:remainder];
}
}else{
[_nextChainNode dispense:amount];
}
}
@end
复制代码
10面额的具体处理者:
//================== DispenseChainNodeFor10Yuan.h ==================
@interface DispenseChainNodeFor10Yuan : DispenseChainNode
@end
//================== DispenseChainNodeFor10Yuan.m ==================
@implementation DispenseChainNodeFor10Yuan
- (void)dispense:(int)amount{
int unit = 10;
if (amount >= unit) {
int count = amount/unit;
int remainder = amount % unit;
NSLog(@"Dispensing %d of %d",count,unit);
if (remainder != 0) {
[_nextChainNode dispense:remainder];
}
}else{
[_nextChainNode dispense:amount];
}
}
@end
复制代码
上面三个具体处理者在dispense:
方法的处理都是相似的:
首先查看当前值是否大于面额
如今咱们建立好了三个具体处理者,咱们再建立一个ATM类来把这些节点串起来:
//================== ATMDispenseChain.h ==================
@interface ATMDispenseChain : NSObject<DispenseProtocol>
@end
//================== ATMDispenseChain.m ==================
@implementation ATMDispenseChain
{
DispenseChainNode *_chainNode;
}
- (instancetype)init{
self = [super init];
if(self){
DispenseChainNodeFor50Yuan *chainNode50 = [[DispenseChainNodeFor50Yuan alloc] init];
DispenseChainNodeFor20Yuan *chainNode20 = [[DispenseChainNodeFor20Yuan alloc] init];
DispenseChainNodeFor10Yuan *chainNode10 = [[DispenseChainNodeFor10Yuan alloc] init];
_chainNode = chainNode50;
[_chainNode setNextChainNode:chainNode20];
[chainNode20 setNextChainNode:chainNode10];
}
return self;
}
- (void)dispense:(int)amount{
NSLog(@"==================================");
NSLog(@"ATM start dispensing of amount:%d",amount);
if (amount %10 != 0) {
NSLog(@"Amount should be in multiple of 10");
return;
}
[_chainNode dispense:amount];
}
@end
复制代码
ATMDispenseChain
这个类在初始化的时候就将三个具体处理者并按照50,20,10的顺序链接起来,并持有一个DispenseChainNode
的指针指向当前的具体处理者(也就是责任链的第一个节点,面额50的具体处理者,由于面额的处理是从50开始的)。
OK,如今咱们把三个具体处理者都封装好了,能够看一下如何使用:
ATMDispenseChain *atm = [[ATMDispenseChain alloc] init];
[atm dispense:230];
[atm dispense:70];
[atm dispense:40];
[atm dispense:10];
[atm dispense:8];
复制代码
建立ATMDispenseChain
的实例后,分别传入一些数值来看一下处理的结果:
==================================
ATM start dispensing of amount:230
Dispensing 4 of 50
Dispensing 1 of 20
Dispensing 1 of 10
==================================
ATM start dispensing of amount:70
Dispensing 1 of 50
Dispensing 1 of 20
==================================
ATM start dispensing of amount:40
Dispensing 2 of 20
==================================
ATM start dispensing of amount:10
Dispensing 1 of 10
==================================
ATM start dispensing of amount:8
Amount should be in multiple of 10
复制代码
从日志的输出能够看出,咱们的责任链处理是没有问题的,针对每一个不一样的数值,ATMDispenseChain
实例都做出了最正确的结果。
须要注意的是,该代码示例中的责任链类(
ATMDispenseChain
)并无在上述责任链模式的成员中。不过此处没必要作过多纠结,咱们在这里只是在业务上稍微多作一点处理罢了。其实也彻底能够不封装这些节点,直接逐个调用setNextChainNode:
方法组装责任链,而后将任务交给第一个处理者便可。
需求完成了,是否能够作个重构?
咱们回去看一下这三个具体处理者在dispense:
方法的处理是很是类似的,他们的区别只有处理的面额数值的不一样:而咱们实际上是建立了针对这三个面值的类,并将面值(50,20,10)硬编码在了这三个类中。这样作是有缺点的,由于若是后面的面额大小变了,或者增长或者减小面额的话咱们会修改这些类或添加删除这些类(即便这也比不使用责任链模式的if-else
要好一些)。
所以咱们能够不建立这些与面额值硬编码的具体处理类,而是在初始化的时候直接将面额值注入到构造方法里面便可!这样一来,咱们能够随意调整和修改面额了。下面咱们作一下这个重构:
首先删除掉三个具体处理者DispenseChainNodeFor50Yuan
,DispenseChainNodeFor20Yuan
,DispenseChainNodeFor10Yuan
。
接着在DispenseChainNode
添加传入面额值的初始化方法以及面额值的成员变量:
//================== ADispenseChainNode.h ==================
@interface DispenseChainNode : NSObject <DispenseProtocol>
{
@protected DispenseChainNode *_nextChainNode;
@protected int _dispenseValue;
}
- (instancetype)initWithDispenseValue:(int)dispenseValue;
- (void)setNextChainNode:(DispenseChainNode *)chainNode;
@end
//================== ADispenseChainNode.m ==================
@implementation DispenseChainNode
- (instancetype)initWithDispenseValue:(int)dispenseValue
{
self = [super init];
if (self) {
_dispenseValue = dispenseValue;
}
return self;
}
- (void)setNextChainNode:(DispenseChainNode *)chainNode{
_nextChainNode = chainNode;
}
- (void)dispense:(int)amount{
if (amount >= _dispenseValue) {
int count = amount/_dispenseValue;
int remainder = amount % _dispenseValue;
NSLog(@"Dispensing %d of %d",count,_dispenseValue);
if (remainder != 0) {
[_nextChainNode dispense:remainder];
}
}else{
[_nextChainNode dispense:amount];
}
}
@end
复制代码
咱们给DispenseChainNode
添加了initWithDispenseValue:
方法后,就能够根据需求随意生成不一样面额的具体处理者了。
接着咱们思考一下以前的ATMDispenseChain
能够作哪些改变?
既然DispenseChainNode
能够根据不一样的面额值生成处理不一样面额的具体处理者实例,那么对于串联多个具体处理者的类ATMDispenseChain
是否是也能够添加一个注入面额数组的初始化方法呢?好比输入[50,20,10]
的数组就能够生成50,20,10面额的具体处理者了;并且数组是有序的,传入数组的元素顺序就能够是责任链中节点的顺序。
思路有了,咱们看一下具体实现:
//================== ATMDispenseChain.m ==================
@implementation ATMDispenseChain
{
DispenseChainNode *_firstChainNode;
DispenseChainNode *_finalChainNode;
int _minimumValue;
}
- (instancetype)initWithDispenseNodeValues:(NSArray *)nodeValues{
self = [super init];
if(self){
NSUInteger length = [nodeValues count];
[nodeValues enumerateObjectsUsingBlock:^(NSNumber * nodeValue, NSUInteger idx, BOOL * _Nonnull stop) {
DispenseChainNode *iterNode = [[DispenseChainNode alloc] initWithDispenseValue:[nodeValue intValue]];
if (idx == length - 1 ) {
_minimumValue = [nodeValue intValue];
}
if (!self->_firstChainNode) {
//because this chain is empty, so the first node and the final node will refer the same node instance
self->_firstChainNode = iterNode;
self->_finalChainNode = self->_firstChainNode;
}else{
//appending the next node, and setting the new final node
[self->_finalChainNode setNextChainNode:iterNode];
self->_finalChainNode = iterNode;
}
}];
}
return self;
}
- (void)dispense:(int)amount{
NSLog(@"==================================");
NSLog(@"ATM start dispensing of amount:%d",amount);
if (amount % _minimumValue != 0) {
NSLog(@"Amount should be in multiple of %d",_minimumValue);
return;
}
[ _firstChainNode dispense:amount];
}
@end
复制代码
重构后的ATMDispenseChain
类新增了initWithDispenseNodeValues:
方法,须要从外部传入面额值的数组。在这个方法里面根据传入的数组构造了整条责任链。
而在dispense:
方法里面则是从责任链的第一个节点来处理面额,并在方法最前面取最小面额的值来作边界处理。
OK,到如今处理者类和责任链类都建立好了,咱们看一下如何使用:
NSArray *dispenseNodeValues = @[@(100),@(50),@(20),@(10)];
ATMDispenseChain *atm = [[ATMDispenseChain alloc] initWithDispenseNodeValues:dispenseNodeValues];
[atm dispense:230];
[atm dispense:70];
[atm dispense:40];
[atm dispense:10];
[atm dispense:8];
复制代码
是否是感受简洁多了?咱们只须要传入一个面额值的数组便可构造出整条责任链并直接使用。来看一下日至输出:
==================================
ATM start dispensing of amount:230
Dispensing 2 of 100
Dispensing 1 of 20
Dispensing 1 of 10
==================================
ATM start dispensing of amount:70
Dispensing 1 of 50
Dispensing 1 of 20
==================================
ATM start dispensing of amount:40
Dispensing 2 of 20
==================================
ATM start dispensing of amount:10
Dispensing 1 of 10
==================================
ATM start dispensing of amount:8
Amount should be in multiple of 10
复制代码
从日志的输出结果上看,咱们重构后的责任链方案没有问题。
下面看一下上面代码对应的类图。
重构前:
重构后:
servlet
中的Filter
能够组成FilterChain
,是责任链模式的一种实践。在状态模式(State Pattern):容许一个对象在其内部状态改变时,改变它的行为。
一个对象存在多个状态,不一样状态下的行为会有不一样,并且状态之间能够相互转换。
若是咱们经过if else
来判断对象的状态,那么代码中会包含大量与对象状态有关的条件语句,并且在添加,删除和更改这些状态的时候回比较麻烦;而若是使用状态模式。将状态对象分散到不一样的类中,则能够消除 if...else
等条件选择语句。
如今咱们清楚了状态模式的适用场景,下面看一下状态模式的成员和类图。
状态模式一共只有四个成员:
下面经过类图来看一下各个成员之间的关系:
模拟一个程序员一天的生活,他有四个状态:
看这几个状态应该是个很是爱写代码的程序员 ^ ^
这个程序员有四个状态,可是有些状态之间是没法切换的:好比从睡觉是没法切换到写代码的(由于须要切换到醒着,而后才能到写代码);从吃饭中是没法切换到醒着的,由于已经醒着了。
若是咱们不使用状态模式,在切换状态的时候可能会写很多if-else
判断,并且随着状态的增多,这些分支会变得更多,难以维护。
而若是咱们使用状态模式,则能够将每一个状态封装到一个类中,便于管理;并且在增长或减小状态时也会很方便。
下面咱们看一下如何用代码来模拟该场景。
首先咱们定义状态类:
//================== State.h ==================
@interface State : NSObject<ActionProtocol>
{
@protected Coder *_coder;
}
- (instancetype)initWithCoder:(Coder *)coder;
@end
//================== State.m ==================
@implementation State
- (instancetype)initWithCoder:(Coder *)coder{
self = [super init];
if (self) {
_coder = coder;
}
return self;
}
@end
复制代码
状态类持有一个coder
,也就是程序员的实例,并遵循了ActionProtocol
:
//================== ActionProtocol.h ==================
@protocol ActionProtocol <NSObject>
@optional;
- (void)wakeUp;
- (void)fallAsleep;
- (void)startCoding;
- (void)startEating;
@end
复制代码
ActionProtocol
定义了程序员的一些动做,这些动做是程序员的平常活动,也是触发状态切换的动做,所以State
也须要遵循这个协议,由于它的子类须要实现这些操做。
接下来咱们看一下State
的子类,根据上面说的四种状态,咱们定义下面四个状态子类:
StateAwake
:
//================== StateAwake.h ==================
@interface StateAwake : State
@end
@implementation StateAwake
- (void)wakeUp{
NSLog(@"Already awake, can not change state to awake again");
}
- (void)startCoding{
NSLog(@"Change state from awake to coding");
[_coder setState:(State *)[_coder stateCoding]];
}
- (void)startEating{
NSLog(@"Change state from awake to eating");
[_coder setState:(State *)[_coder stateEating]];
}
- (void)fallAsleep{
NSLog(@"Change state from awake to sleeping");
[_coder setState:(State *)[_coder stateSleeping]];
}
@end
复制代码
StateSleeping
:
//================== StateSleeping.h ==================
@interface StateSleeping : State
@end
//================== StateSleeping.m ==================
@implementation StateSleeping
- (void)wakeUp{
NSLog(@"Change state from sleeping to awake");
[_coder setState:(State *)[_coder stateAwake]];
}
- (void)startCoding{
NSLog(@"Already sleeping, can not change state to coding");
}
- (void)startEating{
NSLog(@"Already sleeping, can change state to eating");
}
- (void)fallAsleep{
NSLog(@"Already sleeping, can not change state to sleeping again");
}
@end
复制代码
StateEating
:
//================== StateEating.h ==================
@interface StateEating : State
@end
//================== StateEating.m ==================
@implementation StateEating
- (void)wakeUp{
NSLog(@"Already awake, can not change state to awake again");
}
- (void)startCoding{
NSLog(@"New idea came out! change state from eating to coding");
[_coder setState:(State *)[_coder stateCoding]];
}
- (void)startEating{
NSLog(@"Already eating, can not change state to eating again");
}
- (void)fallAsleep{
NSLog(@"Too tired, change state from eating to sleeping");
[_coder setState:(State *)[_coder stateSleeping]];
}
@end
复制代码
"StateCoding":
//================== StateCoding.h ==================
@interface StateCoding : State
@end
//================== StateCoding.m ==================
@implementation StateCoding
- (void)wakeUp{
NSLog(@"Already awake, can not change state to awake again");
}
- (void)startCoding{
NSLog(@"Already coding, can not change state to coding again");
}
- (void)startEating{
NSLog(@"Too hungry, change state from coding to eating");
[_coder setState:(State *)[_coder stateEating]];
}
- (void)fallAsleep{
NSLog(@"Too tired, change state from coding to sleeping");
[_coder setState:(State *)[_coder stateSleeping]];
}
@end
复制代码
从上面的类能够看出,在有些状态之间的转换是失效的,有些是能够的。 好比相同状态的切换是无效的;从 sleeping
没法切换到coding
,可是反过来能够,由于可能写代码累了就直接睡了。
下面咱们看一下程序员类的实现:
//================== Coder.h ==================
@interface Coder : NSObject<ActionProtocol>
@property (nonatomic, strong) StateAwake *stateAwake;
@property (nonatomic, strong) StateCoding *stateCoding;
@property (nonatomic, strong) StateEating *stateEating;
@property (nonatomic, strong) StateSleeping *stateSleeping;
- (void)setState:(State *)state;
@end
//================== Coder.m ==================
@implementation Coder
{
State *_currentState;
}
- (instancetype)init{
self = [super init];
if (self) {
_stateAwake = [[StateAwake alloc] initWithCoder:self];
_stateCoding = [[StateCoding alloc] initWithCoder:self];
_stateEating = [[StateEating alloc] initWithCoder:self];
_stateSleeping = [[StateSleeping alloc] initWithCoder:self];
_currentState = _stateAwake;
}
return self;
}
- (void)setState:(State *)state{
_currentState = state;
}
- (void)wakeUp{
[_currentState wakeUp];
}
- (void)startCoding{
[_currentState startCoding];
}
- (void)startEating{
[_currentState startEating];
}
- (void)fallAsleep{
[_currentState fallAsleep];
}
@end
复制代码
从上面的代码咱们能够看到,程序员类持有一个当前的状态的实例,在初始化后默认的状态为awake
,并对外提供一个setState:
的方法来切换状态。并且在初始化方法里,咱们实例化了全部的状态,目的是在切换状态中时使用,详见具体状态类的方法:
- (void)startEating{
NSLog(@"Too hungry, change state from coding to eating");
[_coder setState:(State *)[_coder stateEating]];
}
复制代码
上面这段代码有点绕,可能须要多看几遍源码才能理解(这里面
[_coder stateEating]
是调用了coder
的一个get
方法,返回了stateEating
这个实例)。
最后,在程序员的动做方法里面,实际上调用的是当前状态对应的方法(这也就是为什么程序员类和状态类都要遵循ActionProtocol
的缘由)。
这样,咱们的状态类,状态子类,程序员类都声明好了。咱们看一下如何使用:
Coder *coder = [[Coder alloc] init];
//change to awake.. failed
[coder wakeUp];//Already awake, can not change state to awake again
//change to coding
[coder startCoding];//Change state from awake to coding
//change to sleep
[coder fallAsleep];//Too tired, change state from coding to sleeping
//change to eat...failed
[coder startEating];//Already sleeping, can change state to eating
//change to wake up
[coder wakeUp];//Change state from sleeping to awake
//change wake up...failed
[coder wakeUp];//Already awake, can not change state to awake again
//change to eating
[coder startEating];//Change state from awake to eating
//change to coding
[coder startCoding];//New idea came out! change state from eating to coding
//change to sleep
[coder fallAsleep];//Too tired, change state from coding to sleeping
复制代码
在上面的代码里,咱们实例化了一个程序员类,接着不断调用一些触发状态改变的方法。咱们把每次状态切换的日至输出注释到了代码右侧,能够看到在一些状态的切换是不容许的:
[coder wakeUp]
:由于程序员对象初始化后默认是awake
状态,因此没法切换到相同的状态[coder startEating]
:在睡觉时是没法直接切换到eating
状态;而在后面wake之后,再执行[coder startEating]
就成功了。从上面的例子能够看出,使用状态模式不须要去写if-else
,并且若是从此想添加一个状态,只须要再建立一个状态子类,并在新的状态子类添加好对全部状态的处理,并在以前的状态子类中添加上对新状态的处理便可。即使咱们修改了以前定义好的状态子类,可是这样也总比使用庞大的if-else
要方便多。
下面看一下上面代码对应的类图。
LifyCycle
是状态模式的一种实现命令模式(Command Pattern):命令(或请求)被封装成对象。客户端将命令(或请求)对象先传递给调用对象。调用对象再把该命令(或请求)对象传给合适的,可处理该命令(或请求)的对象来作处理。
由定义能够看出,在命令模式中,命令被封装成了对象,而发送命令的客户端与处理命令的接收者中间被调用对象隔开了,这种设计的缘由或者适用的场景是什么样的呢?
在有些场景下,任务的处理可能不是须要当即执行的:可能须要记录(日至),撤销或重试(网络请求)。那么在这些场景下,若是任务的请求者和执行者是紧耦合状态下的话就可能会将不少其余执行策略的代码和当即执行的代码混合到一块儿。
这些其余执行策略,咱们暂时称之为控制和管理策略,而若是咱们若是想控制和管理请求,就须要:
所以命令模式就是为此场景量身打造的,它经过:
如今咱们清楚了命令模式的适用场景,下面看一下命令模式的成员和类图。
不包括请求的发起者(客户端),命令模式共有四个成员:
下面经过类图来看一下命令模式各个成员之间的关系:
模拟一个使用遥控器开灯和关灯的例子。
在这个例子中,使用遥控器的人就是客户端,TA发起开启或关闭灯的命令给遥控器(调用者)。而后调用者将命令传递给接收者(灯)。
在这里,人是不直接接触灯的,开启和关闭的命令是经过遥控器来作的转发,最后传达给灯来执行。
下面咱们看一下如何用代码来模拟该场景。
首先咱们建立接收者,灯类:
//================== Light.h ==================
@interface Light : NSObject
- (void)lightOn;
- (void)lightOff;
@end
//================== Light.m ==================
@implementation Light
- (void)lightOn{
NSLog(@"Light on");
}
- (void)lightOff{
NSLog(@"Light off");
}
@end
复制代码
灯类声明并实现了两个接口:开灯接口和关灯接口,来让外部执行开灯和关灯的操做。
接着咱们建立抽象命令类和具体命令类:
抽象命令类:
//================== Command.h ==================
@interface Command : NSObject
- (void)excute;
@end
//================== Command.m ==================
@implementation Command
@end
复制代码
抽象命令类声明了一个执行命令的接口excute
,这个接口由它的子类,也就是具体命令类来实现。
由于这里面只有开灯和关灯两种命令,因此咱们建立两个具体命令类来继承上面的抽象命令类:
开灯命令CommandLightOn
:
//================== CommandLightOn.h ==================
@interface CommandLightOn : Command
- (instancetype)initWithLight:(Light *)light;
@end
//================== CommandLightOn.m ==================
@implementation CommandLightOn
{
Light *_light;
}
- (instancetype)initWithLight:(Light *)light{
self = [super init];
if (self) {
_light = light;
}
return self;
}
- (void)excute{
[_light lightOn];
}
复制代码
关灯命令CommandLightOff
:
//================== CommandLightOff.h ==================
@interface CommandLightOff : Command
- (instancetype)initWithLight:(Light *)light;
@end
//================== CommandLightOff.m ==================
@implementation CommandLightOff
{
Light *_light;
}
- (instancetype)initWithLight:(Light *)light{
self = [super init];
if (self) {
_light = light;
}
return self;
}
- (void)excute{
[_light lightOff];
}
复制代码
咱们能够看到这两个具体命令类分别以本身的方式实现了它们的父类声明的
excute
接口。
最后咱们建立连接客户端和接收者的调用者类,也就是遥控器类RemoteControl
:
//================== RemoteControl.h ==================
@interface RemoteControl : NSObject
- (void)setCommand:(Command *)command;
- (void)pressButton;
@end
//================== RemoteControl.m ==================
@implementation RemoteControl
{
Command *_command;
}
- (void)setCommand:(Command *)command{
_command = command;
}
- (void)pressButton{
[_command excute];
}
@end
复制代码
遥控器类使用set
方法注入了具体命令类,并向外提供了pressButton
这个方法来内部调用已传入的具体命令类的excute
方法。
最后咱们看一下客户端是如何操做这些类的:
//================== client ==================
//init Light and Command instance
//inject light instance into command instance
Light *light = [[Light alloc] init];
CommandLightOn *co = [[CommandLightOn alloc] initWithLight:light];
//set command on instance into remote control instance
RemoteControl *rm = [[RemoteControl alloc] init];
[rm setCommand:co];
//excute command(light on command)
[rm pressButton];
//inject light instance into command off instance
CommandLightOff *cf = [[CommandLightOff alloc] initWithLight:light];
//change to off command
[rm setCommand:cf];
//excute command(light close command)
[rm pressButton];
复制代码
看一下日至输出:
[11851:1190777] Light on
[11851:1190777] Light off
复制代码
从上面的代码能够看到,咱们首先准备好具体命令类的实例,而后将其传递给遥控器类,最后触发遥控器的pressButton
方法来间接触发light
对象的相应操做。
下面看一下上面代码对应的类图。
java.lang.Runnable
是使用命令模式的经典场景,Runnable接口能够做为抽象的命令,而实现了Runnable的线程便是具体的命令。观察者模式(Observer Pattern):定义对象间的一种一对多的依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象均可以到通知并作相应针对性的处理。
凡是涉及到一对一或者一对多的对象交互场景均可以使用观察者模式。一般咱们使用观察者模式实现一个对象的改变会令其余一个或多个对象发生改变的需求,好比换肤功能,监听列表滚动的偏移量等等。
如今咱们清楚了观察者模式的适用场景,下面看一下观察者模式的成员和类图。
观察者模式有四个成员:
下面经过类图来看一下各个成员之间的关系:
模拟这样的一个场景:客户(投资者)订阅理财顾问的建议购买不一样价格的股票。当价格信息变化时,全部客户会收到通知(可使短信,邮件等等),随后客户查看最新数据并进行操做。
一个理财顾问可能服务于多个客户,并且消息须要及时传达到各个客户那边;而客户接收到这些消息后,须要对这些消息作出相应的措施。这种一对多的通知场景咱们可使用观察者模式:理财顾问是被观察的目标(Subject),而TA的客户则是观察者(Observer)。
下面咱们看一下如何用代码来模拟该场景。
首先咱们定义观察者Observer
:
//================== Observer.h ==================
@interface Observer : NSObject
{
@protected Subject *_subject;
}
- (instancetype)initWithSubject:(Subject *)subject;
- (void)update;
@end
//================== Observer.m ==================
@implementation Observer
- (instancetype)initWithSubject:(Subject *)subject{
self = [super init];
if (self) {
_subject = subject;
[_subject addObserver:self];
}
return self;
}
- (void)update{
NSLog(@"implementation by subclasses");
}
复制代码
Observer
类是具体观察者的父类,它声明了一个传入目标类(Subject
)的构造方法并在构造方法里持有这个传入的实例。并且在这个构造方法里,调用了Subject
的‘添加观察者’的方法,即addObserver:
,目的是将当前的观察者实例放入Subject
的用来保存观察者实例的集合中(具体操做能够在下面讲解Subject
类的部分看到)
另外它也定义了update
方法供子类使用。
下面咱们看一下具体观察者类Investor
:
//================== Investor.h ==================
@interface Investor : Observer
@end
//================== Investor.m ==================
@implementation Investor
- (void)update{
float buyingPrice = [_subject getBuyingPrice];
NSLog(@"investor %p buy stock of price:%.2lf",self,buyingPrice);
}
@end
复制代码
具体观察者实现了该协议中定义的方法update
方法,在这个方法里面,首先经过getBuyingPrice
方法得到到最新的在监听的数据buyingPrice
,而后再作其余操做。这里为了方便展现,直接使用日至打印出当前的具体观察者实例的内存地址和当前监听的最新值。
下面咱们声明一下目标类和具体目标类:
目标类Subject
//================== Subject.h ==================
@interface Subject : NSObject
{
@protected float _buyingPrice;
@protected NSMutableArray <Observer *>*_observers;
}
- (void)addObserver:(Observer *) observer;
- (void)removeObserver:(Observer *) observer;
- (void)notifyObservers;
- (void)setBuyingPrice:(float)price;
- (double)getBuyingPrice;
@end
//================== Subject.m ==================
@implementation Subject
- (instancetype)init{
self = [super init];
if (self) {
_observers = [NSMutableArray array];
}
return self;
}
- (void)addObserver:( Observer * ) observer{
[_observers addObject:observer];
}
- (void)removeObserver:( Observer *) observer{
[_observers removeObject:observer];
}
- (void)notifyObservers{
[_observers enumerateObjectsUsingBlock:^(Observer * _Nonnull observer, NSUInteger idx, BOOL * _Nonnull stop) {
[observer update];
}];
}
- (void)setBuyingPrice:(float)price{
_buyingPrice = price;
[self notifyObservers];
}
- (double)getBuyingPrice{
return _buyingPrice;
}
@end
复制代码
目标类持有一个可变数组,用来保存观察本身的观察者们;而且还提供了增长,删除观察者的接口,也提供了通知全部观察者的接口。
并且它持有一个数据buyingPrice
,这个数据就是让外部观察者观察的数据。尤为注意它向外界提供的setBuyingPrice:
方法:当外部调用这个方法,也就是要更新buyingPrice
这个数据时,目标类调用了notifyObservers
方法来告知当前全部观察本身的观察者们:我更新了。
而getBuyingPrice
就是用来返回当前的buyingPrice
的值的,通常是在观察者们收到更新通知后,主动调动这个方法获取的(具体看上面Investor
类的实现)。
OK,如今抽象目标类定义好了,下面咱们看一下具体目标类FinancialAdviser
:
//================== FinancialAdviser.h ==================
@interface FinancialAdviser : Subject
@end
//================== FinancialAdviser.m ==================
@implementation FinancialAdviser
@end
复制代码
由于全部的接口的事先已经在Subject
类定义好了,因此咱们只需新建一个咱们须要的子类便可(若是有不一样于父类的操做的话仍是能够按照本身的方式定义)。
下面咱们看一下观察者的机制是如何实现的:
FinancialAdviser *fa = [[FinancialAdviser alloc] init];
Investor *iv1 = [[Investor alloc] initWithSubject:fa];
NSLog(@"====== first advice ========");
[fa setBuyingPrice:1.3];
Investor *iv2 = [[Investor alloc] initWithSubject:fa];
Investor *iv3 = [[Investor alloc] initWithSubject:fa];
NSLog(@"====== second advice ========");
[fa setBuyingPrice:2.6];
复制代码
从代码中能够看到,咱们最开始向FinancialAdviser
(具体目标类)添加了一个具体观察者类的实例iv1
,而后FinancialAdviser
的实例fa
便通知了全部观察者(此时的观察者只有iv1
)。
后面咱们继续向fa
添加了iv2
和iv3
后发送通知。此时三个观察者都收到了消息。
在下面的日至输出中也能够看到,内存地址0x600003094c00
就是iv1
,0x600003083680
和0x600003083690
就是iv2
和iv3
。
====== first advice ========
investor 0x600003094c00 buy stock of price:1.30
====== second advice ========
investor 0x600003094c00 buy stock of price:2.60
investor 0x600003083680 buy stock of price:2.60
investor 0x600003083690 buy stock of price:2.60
复制代码
下面看一下上面代码对应的类图。
java.util
包中,提供了Observable
类以及Observer
接口,它们构成了Java语言对观察者模式的支持。中介者模式(Mediator Pattern):用一个中介对象来封装一系列的对象交互,中介者使各对象之间不须要显式地相互引用,从而使其耦合松散,并且能够独立地改变它们之间的交互。
系统结构可能会日益变得复杂,对象之间存在大量的相互关联和调用,系统的总体结构容易变为网状结构。在这种状况下,若是须要修改某一个对象,则可能会要跟踪和该对象关联的其余全部对象,并进行处理。耦合越多,修改的地方就会越多。
若是咱们使用中介者对象,则能够将系统的网状结构变成以中介者为中心的星型结构。中介者承担了中转做用和协调做用,简化了对象之间的交互,并且还能够给对象间的交互进行进一步的控制。
如今咱们清楚了中介者模式的适用场景,下面看一下中介者模式的成员和类图。
中介者模式一共有四个成员:
模拟一个多人对话的场景:当一我的发出消息后,另外的那些人能够收到该消息。
假设一共有A,B,C三我的,那么当A发出消息后,须要分别传递给B,C二人。若是三我的直接相互通讯,可能伪代码会是这样的:
A sent message to B
A sent message to C
复制代码
并且随着人数的增多,代码行数也会变多,这显然是不合理的。
所以在这种场景下,咱们须要使用中介者模式,在全部人中间来作一个消息的多路转发:当A发出消息后,由中介者来发送给B和C:
A sent message to Mediator ;
Mediator sent message to B & C
复制代码
下面咱们看一下如何用代码来模拟该场景。
首先咱们建立通话的用户类User
:
//================== User.h ==================
@interface User : NSObject
- (instancetype)initWithName:(NSString *)name mediator:(ChatMediator *)mediator;
- (void)sendMessage:(NSString *)message;
- (void)receivedMessage:(NSString *)message;
@end
//================== User.m ==================
@implementation User
{
NSString *_name;
ChatMediator *_chatMediator;
}
- (instancetype)initWithName:(NSString *)name mediator:(ChatMediator *)mediator{
self = [super init];
if (self) {
_name = name;
_chatMediator = mediator;
}
return self;
}
- (void)sendMessage:(NSString *)message{
NSLog(@"================");
NSLog(@"%@ sent message:%@",_name,message);
[_chatMediator sendMessage:message fromUser:self];
}
- (void)receivedMessage:(NSString *)message{
NSLog(@"%@ has received message:%@",_name,message);
}
@end
复制代码
用户类在初始化的时候须要传入中介者的实例,并持有。目的是为了在后面发送消息的时候把消息转发给中介者。
另外,用户类还对外提供了发送消息和接收消息的接口。而在发送消息的方法内部其实调用的是中介者的发送消息的方法(由于中介者持有了全部用户的实例,所以能够作多路转发),具体是如何作的咱们能够看下中介者类ChatMediator
的实现:
//================== ChatMediator.h ==================
@interface ChatMediator : NSObject
- (void)addUser:(User *)user;
- (void)sendMessage:(NSString *)message fromUser:(User *)user;
@end
//================== ChatMediator.m ==================
@implementation ChatMediator
{
NSMutableArray <User *>*_userList;
}
- (instancetype)init{
self = [super init];
if (self) {
_userList = [NSMutableArray array];
}
return self;
}
- (void)addUser:(User *)user{
[_userList addObject:user];
}
- (void)sendMessage:(NSString *)message fromUser:(User *)user{
[_userList enumerateObjectsUsingBlock:^(User * _Nonnull iterUser, NSUInteger idx, BOOL * _Nonnull stop) {
if (iterUser != user) {
[iterUser receivedMessage:message];
}
}];
}
@end
复制代码
中介者类提供了addUser:
的方法,所以咱们能够不断将用户添加到这个中介者里面(能够看作是注册行为或是“加入群聊”)。在每次加入一个User
实例后,都将这个实例添加到中介者持有的这个可变数组里。因而在未来中介者就能够经过遍历数组的方式来作消息的多路转发,具体实现能够看sendMessage:fromUser:
这个方法。
到如今为止,用户类和中介者类都建立好了,咱们看一下消息是如何转发的:
ChatMediator *cm = [[ChatMediator alloc] init];
User *user1 = [[User alloc] initWithName:@"Jack" mediator:cm];
User *user2 = [[User alloc] initWithName:@"Bruce" mediator:cm];
User *user3 = [[User alloc] initWithName:@"Lucy" mediator:cm];
[cm addUser:user1];
[cm addUser:user2];
[cm addUser:user3];
[user1 sendMessage:@"happy"];
[user2 sendMessage:@"new"];
[user3 sendMessage:@"year"];
复制代码
从代码中能够看到,咱们这里建立了三个用户,分别加入到了聊天中介者对象里。再后面咱们分别让每一个用户发送了一条消息。咱们下面经过日至输出来看一下每一个用户的消息接收状况:
[13806:1284059] ================
[13806:1284059] Jack sent message:happy
[13806:1284059] Bruce has received message:happy
[13806:1284059] Lucy has received message:happy
[13806:1284059] ================
[13806:1284059] Bruce sent message:new
[13806:1284059] Jack has received message:new
[13806:1284059] Lucy has received message:new
[13806:1284059] ================
[13806:1284059] Lucy sent message:year
[13806:1284059] Jack has received message:year
[13806:1284059] Bruce has received message:year
复制代码
下面看一下上面代码对应的类图。
Timer
就是中介者类的实现,而配合使用的TimerTask
则是同事类的实现。到这里设计模式中的行为型模式就介绍完了,读者能够结合UML类图和demo的代码来理解每一个设计模式的特色和相互之间的区别,但愿读者能够有所收获。
本篇博客的代码和类图都保存在个人GitHub库中:knightsj:object-oriented-design中的 Chapter 2.3。
到本篇为止,面向对象设计系列暂时告一段落,短时间内不会有新的文章出来。读者朋友们能够随时给我提意见或沟通。
本篇已同步到我的博客:传送门
该系列前面的三篇文章: