大部分公司都有销售团队,假设老板给你布置了一个任务,让你按照下面的要求开发一套程序来计算销售团队每月的工资。javascript
每一个人的工资就是基本工资加上奖金,那么按照常规模式咱们来看下如何让实现。java
#import "calculateBonus.h"
@implementation calculateBonus
-(NSInteger)calculateSalary:(NSInteger)monthSales sumSales:(NSInteger)sumSales isManager:(BOOL)manager{
//基本工资都是8000
NSInteger salary = 8000;
salary += [self monthBonus:monthSales];
salary += [self sumBonus:sumSales];
if (manager) {
salary += [self groupBonus];
}
return salary;
}
//当月奖金
-(NSInteger)monthBonus:(NSInteger)monthSales{
return monthSales * 0.003;
}
//累积奖金
-(NSInteger)sumBonus:(NSInteger)sumSales{
return sumSales * 0.001;
}
//团队奖金
-(NSInteger)groupBonus{
//简单起见,团队的销售总额设置为100000
return 100000 * 0.01;
}
@end复制代码
测试下:ios
calculateBonus *calculate = [calculateBonus new];
NSInteger salary1 = [calculate calculateSalary:12222 sumSales:12000 isManager:YES];
NSLog(@"经理工资:%zd", salary1);
NSInteger salary2 = [calculate calculateSalary:21333 sumSales:23111 isManager:NO];
NSLog(@"员工甲:%zd", salary2);
NSInteger salary3 = [calculate calculateSalary:22113 sumSales:11222 isManager:NO];
NSLog(@"员工乙:%zd", salary3);复制代码
输出:编程
2016-12-14 08:57:58.600 装饰者模式[64313:1880733] 经理工资:9048
2016-12-14 08:57:58.601 装饰者模式[64313:1880733] 员工甲:8086
2016-12-14 08:57:58.601 装饰者模式[64313:1880733] 员工乙:8077
Program ended with exit code: 0复制代码
看起来运行良好,好,该来的仍是来了,该需求。设计模式
如今要增长一个环比奖金:就是本月销售额比上月又增长,而后达到必定比例,就会有奖金,增长越多,奖金比率越高。你说这还不简单,再加一个环比奖金计算方法不就是了。那么若是工资计算的方式也换了呢?不一样级别的人员或者员工的计算奖金的方式也换了呢?安全
假设app
甲的总工资 = 基本工资 + 当月销售奖金 + 环比奖金性能
乙的工资 = 基本工资 + 环比奖金学习
丙的工资 = 基本工资 + 当月销售奖金测试
丁的工资 = 基本工资 + 环比奖金 + 团队奖金
后面再给你制定十几种不一样的计算方式,崩溃了没有?按照上面的写法,那么每一种总工资计算方式你都要写一种方法,再假设这些人的总工资计算方式每一个季度还会有调整,你怎么办,接着改?
仔细分析下上面的需求,总工资的计算有两部分:基本工资加上各类奖金,基本工资是固定的,麻烦的地方就在于奖金的计算方式是动态变化的,有各类各样的组合方式。按照设计模式的思想:封装变化,这里变化的部分是奖金的组合方式,那么咱们就把这部分封装起来。
咱们能够采起这样的方式,把每张奖金的计算方式都单独成类,而后须要用到哪一种奖金计算,就把这个奖金计算和基本工资组合起来,须要多少种奖金计算方式,就组合多少种。这样实现起来,是否是很是灵活?之后你想修改或者增长减小奖金计算方式,只须要修改或者增长减小一个奖金计算方式就能够了,至于每一个人的总工资计算方式各不相同,就更简单了,交给客户端自由组合。
总结起来就是以下三个要求:
奖金计算方式要灵活,能够动态增长或者减小
能够动态组合奖金计算方式
要实现上面的功能,就要清楚咱们今天的猪脚:装饰器模式。
下面来认识下这位仁兄
动态地给一个对象添加一些额外的职责。就增长功能来讲, 装饰模式比生成子类更为灵活。
通常咱们在给一个本来的类添加功能的时候,都会想到使用继承,在原有的类上扩展新的功能,可是继承有一个很是大的缺点:和父类耦合性过高。若是后续须要添加或者减小功能,就不得不每次都要修改子类,并且若是修改了父类,对子类的影响也很是大。
因此咱们通常优先考虑使用组合来实现功能的扩展,这也是设计模式的一个原则:多用组合,少用继承。装饰器模式就是实现组合功能的一种方式,它能够透明的给本来的类增长或者减小功能,并且能够把多个功能组合在一块儿,他不会改变原有类的功能,只是在原来功能的基础上加上一些新功能,而这些操做被装饰对象是绝不知情的。
好比上面的计算总工资,原有的对象是基本工资,可是须要在基本工资的基础上加上各类奖金,也就是给基本工资扩展了功能,可是基本工资这个原有功能是不会改变的,只是给他加上了各类各样的奖金,丰富了它的功能,最后算出来的仍是工资,也就是保持原有的类型(整数型)不改变,这点要切记。
实现装饰器模式要注意以下几点:
1.接口的一致性
装饰对象的接口必须与它所装饰的Component的接口是一致的,所以,全部的concreteDecorator类必须有一个公共的父类
2.省略抽象的Decorator类
当你仅须要添加一个职责时,没有必要定义抽象Decorator类。你经常须要处理现存的类层次结构而不是设计一个新系统,这时你能够把Decorator向Component转发请求的职责合并到ConcreteDecorator中。
3.保持Component类的简单性
为了保证接口的一致性,组件和装饰必须有一个公共的Component父类。所以保持这个类的简单性是很重要的;即,它应集中于定义接口而不是存储数据。对数据表示的定义应延迟到子类中,不然Component类会变得过于复杂和庞大,于是难以大量使用。赋予Component太多的功能也使得,具体的子类有一些它们并不须要的功能的可能性大大增长。
4.改变对象外壳与改变对象内核
咱们能够将Decorator看做一个对象的外壳,它能够改变这个对象的行为。另一种方法是改变对象的内核。例如,Strategy模式就是一个用于改变内核的很好的模式。
当Component类本来就很庞大时,使用Decorator模式代价过高,Strategy模式相对更好一些。在Strategy模式中,组件将它的一些行为转发给一个独立的策略对象,咱们能够替换strategy对象,从而改变或扩充组件的功能。
先定义一个抽象基类,工资类和奖金计算方式类都继承自这个类,该类定义了一个公开接口,用于计算奖金
#import <Foundation/Foundation.h>
@interface component : NSObject
-(NSInteger)calculateSalary:(NSInteger)monthSales sumSales:(NSInteger)sumSales;
@end
=================复制代码
#import "component.h"
@interface concreteComponent : component
@end
======================
//被装饰对象,基本工资
#import "concreteComponent.h"
@implementation concreteComponent
-(NSInteger)calculateSalary:(NSInteger)monthSales sumSales:(NSInteger)sumSales{
//基本工资8000
return 8000;
}
@end复制代码
定义一个抽象装饰器,继承自抽象基类component,每一个具体的装饰器继承自该类,该类主要作一些初始化工做
#import "component.h"
@interface Decorator : component
@property(strong,nonatomic)component *components;
- (instancetype)initWithComponet:(component *)component;
@end
=================
#import "Decorator.h"
@implementation Decorator
- (instancetype)initWithComponet:(component *)component
{
self = [super init];
if (self) {
self.components = component;
}
return self;
}
-(NSInteger)calculateSalary:(NSInteger)monthSales sumSales:(NSInteger)sumSales{
return [self.components calculateSalary:monthSales sumSales:sumSales];
}
@end复制代码
#import "Decorator.h"
@interface monthBonusDecorator : Decorator
@end
==================
#import "monthBonusDecorator.h"
@implementation monthBonusDecorator
-(NSInteger)calculateSalary:(NSInteger)monthSales sumSales:(NSInteger)sumSales{
NSInteger salary = [self.components calculateSalary:monthSales sumSales:sumSales];
NSInteger bonus = monthSales * 0.03;
NSLog(@"当月销售奖金:%zd", bonus);
return salary += bonus;
}
@end复制代码
#import "Decorator.h"
@interface sumBonusDecatorator : Decorator
@end
================
#import "sumBonusDecatorator.h"
@implementation sumBonusDecatorator
-(NSInteger)calculateSalary:(NSInteger)monthSales sumSales:(NSInteger)sumSales{
NSInteger salary = [self.components calculateSalary:monthSales sumSales:sumSales];
NSInteger bonus = sumSales * 0.01;
NSLog(@"累积销售奖金:%zd", bonus);
return salary += bonus;
}
@end复制代码
#import "Decorator.h"
@interface groupBonusDecorator : Decorator
@end
=================
#import "groupBonusDecorator.h"
@implementation groupBonusDecorator
-(NSInteger)calculateSalary:(NSInteger)monthSales sumSales:(NSInteger)sumSales{
NSInteger salary = [self.components calculateSalary:monthSales sumSales:sumSales];
NSInteger bonus = 100000 * 0.01;
NSLog(@"团队奖金:%zd", bonus);
return salary += bonus;
}
@end复制代码
//基本工资,被装饰对象
component *c1 = [concreteComponent new];
//装饰器
Decorator *d1 = [[monthBonusDecorator alloc]initWithComponet:c1];
Decorator *d2 = [[sumBonusDecatorator alloc]initWithComponet:d1];
NSInteger salary1 = [d2 calculateSalary:10000 sumSales:12212];
NSLog(@"\n奖金组合方式:当月销售奖金 + 累积销售奖金 \n 总工资 = %zd", salary1);
NSLog(@"\n=============================================================================");
Decorator *d3 = [[monthBonusDecorator alloc]initWithComponet:c1];
Decorator *d4 = [[sumBonusDecatorator alloc]initWithComponet:d3];
Decorator *d5 = [[groupBonusDecorator alloc]initWithComponet:d4];
NSInteger salary2 = [d5 calculateSalary:12100 sumSales:12232];
NSLog(@"\n奖金组合方式:当月销售奖金 + 累积销售奖金 + 团队奖金 \n 总工资 = %zd", salary2);
NSLog(@"\n=============================================================================");
Decorator *d6 = [[monthBonusDecorator alloc]initWithComponet:c1];
Decorator *d7 = [[groupBonusDecorator alloc]initWithComponet:d6];
NSInteger salary3 = [d7 calculateSalary:23111 sumSales:231111];
NSLog(@"\n奖金组合方式:当月销售奖金 + 团队奖金 \n 总工资 = %zd", salary3);复制代码
2016-12-14 10:34:31.280 装饰者模式[64586:1944336] 当月销售奖金:300
2016-12-14 10:34:31.280 装饰者模式[64586:1944336] 累积销售奖金:122
2016-12-14 10:34:31.280 装饰者模式[64586:1944336]
奖金组合方式:当月销售奖金 + 累积销售奖金
总工资 = 8422
=============================================================================
2016-12-14 10:34:31.280 装饰者模式[64586:1944336] 当月销售奖金:363
2016-12-14 10:34:31.280 装饰者模式[64586:1944336] 累积销售奖金:122
2016-12-14 10:34:31.280 装饰者模式[64586:1944336] 团队奖金:1000
2016-12-14 10:34:31.280 装饰者模式[64586:1944336]
奖金组合方式:当月销售奖金 + 累积销售奖金 + 团队奖金
总工资 = 9485
=============================================================================
2016-12-14 10:34:31.281 装饰者模式[64586:1944336] 当月销售奖金:693
2016-12-14 10:34:31.281 装饰者模式[64586:1944336] 团队奖金:1000
2016-12-14 10:34:31.281 装饰者模式[64586:1944336]
奖金组合方式:当月销售奖金 + 团队奖金
总工资 = 9693复制代码
从上面的测试能够看出,不论是使用何种奖金组合方式,只须要调用对应的装饰器便可,很是灵活。经过上面的代码咱们看到,装饰器是一层层包裹的,基本工资被月工资装饰器包裹,月工资装饰器被累积奖金装饰器包裹,累积装饰器被团队奖金装饰器包裹,当调用计算奖金的公式的时候,就会按照顺序层层递归调用每一个装饰器的功能,到最后算出总工资,咱们来用示意图看看调用过程。
因为每一个装饰器之间是彻底独立的,因此咱们可使用任何咱们想要的方式去组合这些装饰器,好比屡次重复调用同一个装饰器,调换装饰器的顺序等等。
在以下状况能够考虑使用对象组合
在不影响其余对象的状况下,以动态、透明的方式给单个对象添加职责。
处理那些能够撤消的职责。
当不能采用生成子类的方法进行扩充时
一种状况是,可能有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增加。另外一种状况多是由于类定义被隐藏,或类定义不能用于生成子类。
比继 承 更 灵 活
与 对 象 的 静 态 继 承 ( 多 重 继 承 ) 相 比 , D e c o r a t o r 模 式 提 供 了 更 加
灵活的向对象添加职责的方式。能够用添加和分离的方法,用装饰在运行时刻增长和删除职 责。相比之下,继承机制要求为每一个添加的职责建立一个新的子类。这会产生许多新的类,而且会增长系统的复杂度。此外,为一 个特定的 C o m p o n e n t 类 提 供 多 个 不 同 的 D e c o r a t o r 类 , 这 就 使 得 你 可 以 对 一 些 职 责 进 行 混 合 和 匹配。
使用 D e c o r a t o r 模式能够很容易地重复添加一个特性。
避 免 了高层次类 有 太 多 的 特 征
D e c o r a t o r模 式 提 供 了 一 种 “ 即 用 即 付 ” 的 方 法来添加职责。它并不试图在一个复杂的可定制的类中支持全部可预见的特征,相反,你可 以定义一个简单的类,而且用 D e c o r a t o r 类 给 它 逐 渐 地 添 加 功 能 。 可 以 从 简 单 的 部 件 组 合 出 复 杂的功能。这样,应用程序没必要为不须要的特征付出代价。同时也更易于不依赖于 D e c o r a t o r 所扩展(甚至是不可预知的扩展)的类而独立地定义新类型的 D e c o r a t o r 。 扩 展 一 个 复 杂 类 的 时候,极可能会暴露与添加的职责无关的细节。
产生 许 多 小 对 象
采用 D e c o r a t o r 模 式 进 行 系 统 设 计 往 往 会 产 生 许 多 看 上 去 类 似 的 小 对 象 , 这些对象仅仅在他们相互链接的方式上有所不一样,而不是它们的类或是它们的属性值有所不 同。尽管对于那些了解这些系统的人来讲,很容易对它们进行定制,可是很难学习这些系统, 排错也很困难。
关于面向切换编程的具体解释看这里
AOP通常用来实现以下功能:日志记录,性能统计,安全控制,事务处理,异常处理等等。将日志记录,性能统计,安全控制,事务处理,异常处理等代码从业务逻辑代码中划分出来,经过对这些行为的分离,咱们但愿能够将它们独立到非业务逻辑的方法中,进而改变这些行为的时候不影响业务逻辑的代码。
看上面的描述就知道,装饰器就是一个很好实现AOP的方式,由于装饰器就是在不改变原有功能的前提下对其进行透明扩展的。
咱们目前有一个鸭子类,实现呱呱叫的一个方法,如今我但愿在不改变原有功能的状况下,统计鸭子叫了多少次,这就是AOP中的日志记录功能,咱们可使用装饰器模式来实现,具体代码我就不贴了,直接看最后的demo。
咱们应该用过一些图片处理app,能够给图片加上各类各样的滤镜或者裁剪旋转图片等等功能,其实这些也可使用装饰器来实现,能够把每一个功能都实现为一个装饰器,而后用户选择使用什么功能,就给图片加上对应的装饰器去作处理,这样作是否是很是灵活?
其实在iOS里面已经为咱们提供了相似装饰器模式的功能的方法:category。category也能够透明的为一个类添加方法,下面我就使用一个小demo来演示如何使用category和装饰者模式分别来实现图片的选择和阴影效果。具体见demo。