设计模式之策略模式(iOS开发,代码用Objective-C展示)
在实际开发过程中,app需求都是由产品那边给出,往往是他给出第一版功能,我们写好代码后,会相应的给出第二版、第三版功能,而这些功能是在实际使用中,根据用户需求而不断增加的。如果在编码之初,我们并未认识到这一点,并未后续添加的代码做好相应的设计准备,那么无疑,这个项目代码会越来越乱,就会导致这样一个循环:
- 产品提需求
- 我根据需求写代码
- 产品增加需求
- 为了在规定时间内完成任务,我根据需要增加的需求增加代码(由于没有思考好相应的设计,使得代码又长又乱)
- 产品再增加需求
- 我再增加代码,由于前面代码设计不合理,使得即使只增加一个小小的功能,我整个项目各个地方都要添加这样的代码
- 产品觉得某个功能不好,要删掉,然后我得在项目中找到各种地方对应功能的代码,删掉,还得撸顺上下的逻辑关系
- 这样子,发现工作量好大、没时间看书、没时间学习,总是撸相同质量的代码,技能得不到提升
- 如果不从自己身上找原因,就开始骂产品,有事没事总增加需求,觉得呆在这公司没意思,然后辞职......
- 下一家公司,又开始这样的循环......
在知乎上看到这样一个问答:
是不是程序员都感觉不幸福?过得很不好?因为不管bat公司还是中小公司,各种加班、天天撸代码,没时间谈女朋友.......
看到这样一个很赞的回答:技术好的,都过得不错,技术差的......
- 现在有这样一个任务,做一个商场收银软件,营业员根据客户所购买的商品的单价和数量,向客户收费。
很简单的一个需求,稍微分析就可以得到下图的界面:
根据界面,也可以很快的将主要代码写出来:
#import <Foundation/Foundation.h> @interface ZYTotalPrices : NSObject
@property (nonatomic, assign) double singlePrices;
@property (nonatomic, assign) int number; - (instancetype)initWithSinglePrices:(NSString *)singlePrices number:(NSString *)number;
- (double)totalPrices;
@end #import "ZYTotalPrices.h" @implementation ZYTotalPrices - (instancetype)initWithSinglePrices:(NSString *)singlePrices number:(NSString *)number
{
if (self = [super init]) {
self.number = [number intValue];
self.singlePrices = [singlePrices doubleValue];
}
return self;
} - (double)totalPrices
{
return self.number * self.singlePrices;
}
@end
然后产品跑过来和你说,现在有新的需求了,商场促销,所有商品打八折。
想想,直接在- (double)totalPrices 方法里面,再乘以0.8就ok了......这样是满足了现有的需求,那要是某天商场打七折或者说,打完折回复原价,怎么办?
2. 增加打折
其实,只需要增加一个下拉选择框,让收银员选择当前的打折就可以了,主要代码如下:
#import <Foundation/Foundation.h> @interface ZYTotalPrices : NSObject
@property (nonatomic, assign) double singlePrices;
@property (nonatomic, assign) int number;
/**
* 下拉菜单中的某个属性字符串
*/
@property (nonatomic, copy) NSString *itemStr; - (instancetype)initWithSinglePrices:(NSString *)singlePrices number:(NSString *)number;
- (double)totalPrices;
@end #import "ZYTotalPrices.h" @interface ZYTotalPrices ()
/**
* 打折语句
*/
@property (nonatomic, strong) NSArray *discountStrs;
/**
* 打折的具体数值
*/
@property (nonatomic, strong) NSArray *discountNumbers; /**
* 打折对应下标
*/
@property (nonatomic, assign) int index;
@end @implementation ZYTotalPrices - (NSArray *)discountNumbers
{
if (!_discountNumbers) {
_discountNumbers = @[@(1), @(0.8), @(0.5)];
}
return _discountNumbers;
} - (NSArray *)discountStrs
{
if (!_discountStrs) {
_discountStrs = @[@"正常收费", @"打八折", @"打五折"]; // 默认为正常收费
self.index = 0;
}
return _discountStrs;
} - (void)setItemStr:(NSString *)itemStr
{
_itemStr = [itemStr copy]; [self.discountStrs enumerateObjectsUsingBlock:^(NSString *obj, NSUInteger idx, BOOL *stop) {
if ([obj isEqualToString:itemStr]) {
self.index = (int)idx;
*stop = YES;
}
}];
} - (instancetype)initWithSinglePrices:(NSString *)singlePrices number:(NSString *)number
{
if (self = [super init]) {
self.number = [number intValue];
self.singlePrices = [singlePrices doubleValue];
}
return self;
} - (double)totalPrices
{
double tempNumber = [self.discountNumbers[self.index] doubleValue];
return self.number * self.singlePrices * tempNumber;
}
@end
差不多就有了下面这样的界面:(请勿完全对照)
这时候,产品过来说,需要增加“满300送100”的活动。这完全是可以在类里多增加一个方法,来计算这个的。但是,这种方法并不好,事实上,
我们完全可以抽出一个抽象基类出来,基类里面有个方法,就是返回总的钱数,而各个不同算法的子类继承自这个基类,实现基类里面的方法,然后用工具类根据不同的需求调用不同的子类,这样的话,分工很明确,如果有什么特殊的需求,找到这个子类增加就ok
3. 简单工厂模式
需要注意的是,其实打八折、七折、六折...完全是可以用个变量来表示的,没必要衍生出很多子类,直接就是一个打折子类,内部确定打折的具体折数;同理,满**减**也是如此。
面向对象编程,并不是类越多越好,类的划分是为了封装,但划分类的基础是抽象,具有相同属性和功能的对象的抽象集合才是类。
打多少折,只是形式的不同,抽象分析出来,所有的打折算法是一样的,所有打折算法应该是一个类。同理,满**减**也是如此。
代码如下:
抽象基类: #import <Foundation/Foundation.h>
@interface ZYTotalPrices : NSObject
@property (nonatomic, assign) double singlePrices;
@property (nonatomic, assign) int number;
/**
* 如此,抽象类里面有一个方法声明,实现交给子类
*
*/
- (double)totalPrices;
@end #import "ZYTotalPrices.h"
@implementation ZYTotalPrices
@end
正常价格: #import "ZYTotalPrices.h" @interface ZYPricesNoraml : ZYTotalPrices @end #import "ZYPricesNoraml.h" @implementation ZYPricesNoraml
/**
* 正常收费
*
*/
- (double)totalPrices
{
return self.singlePrices * self.number;
}
@end
打折: #import "ZYTotalPrices.h" @interface ZYPricesDiscount : ZYTotalPrices
/**
* 打折率,为小数
*/
@property (nonatomic, assign) double moneyRebate; - (instancetype)initWithMoneyRebate:(double)moneyRebate;
@end #import "ZYPricesDiscount.h" @implementation ZYPricesDiscount - (instancetype)initWithMoneyRebate:(double)moneyRebate
{
if (self = [super init]) {
self.moneyRebate = moneyRebate;
}
return self;
} - (double)totalPrices
{
return self.number * self.singlePrices * self.moneyRebate;
}
@end
返利: #import "ZYTotalPrices.h" @interface ZYPricesReturn : ZYTotalPrices
/**
* 返利条件
*/
@property (nonatomic, assign) double moneyCondition; /**
* 返利值
*/
@property (nonatomic, assign) double moneyReturn; - (instancetype)initWithMoneyCondition:(double)moneyCondition moneyReturn:(double)moneyReturn;
@end #import "ZYPricesReturn.h" @implementation ZYPricesReturn
- (instancetype)initWithMoneyCondition:(double)moneyCondition moneyReturn:(double)moneyReturn
{
if (self = [super init]) {
self.moneyCondition = moneyCondition;
self.moneyReturn = moneyReturn;
}
return self;
} - (double)totalPrices
{
double total = self.number * self.singlePrices;
return total - (int)(total / self.moneyCondition) * self.moneyReturn;
}
@end
工具类: #import <Foundation/Foundation.h> @class ZYTotalPrices;
@interface ZYPricesTool : NSObject
+ (ZYTotalPrices *)createTotalPricesWithItemStr:(NSString *)itemStr;
@end #import "ZYPricesTool.h"
#import "ZYPricesNoraml.h"
#import "ZYPricesDiscount.h"
#import "ZYPricesReturn.h" static NSArray *_arrayStrs;
@implementation ZYPricesTool
+ (NSArray *)arrayStrs
{
if (_arrayStrs == nil) {
_arrayStrs = @[@"正常收费", @"满300返100", @"打八折"];
}
return _arrayStrs;
} + (ZYTotalPrices *)createTotalPricesWithItemStr:(NSString *)itemStr
{
[self arrayStrs]; __block int index = 0; [_arrayStrs enumerateObjectsUsingBlock:^(NSString *obj, NSUInteger idx, BOOL *stop) {
if ([obj isEqualToString:itemStr]) {
index = (int)idx;
*stop = YES;
}
}]; switch (index) {
case 0:
return [[ZYPricesNoraml alloc] init];
break;
case 1:
return [[ZYPricesReturn alloc] initWithMoneyCondition:300 moneyReturn:100];
break;
case 2:
return [[ZYPricesDiscount alloc] initWithMoneyRebate:0.8];
break;
}
return nil;
} @end
控制器内代码: #import "ViewController.h"
#import "ZYPricesTool.h"
#import "ZYPricesNoraml.h"
#import "ZYPricesDiscount.h"
#import "ZYPricesReturn.h"
@interface ViewController () @end @implementation ViewController - (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib. ZYTotalPrices *totalPrices = [ZYPricesTool createTotalPricesWithItemStr:@"打八折"]; totalPrices.number = 5;
totalPrices.singlePrices = 6; NSLog(@"%lf",[totalPrices totalPrices]);
}
@end
这次,不论产品跑过要求怎么修改,都可以很简单的实现了。比如说,要“满500返200”,直接在ZYPricesTool里面增加一个就好,算法完全不需要重新写。如果又有一种需求,"满100元返积分10点,一次购物到一定积分,赠送礼物",直接产生一个继承自ZYTotalPrices的子类,在里面写产生积分的算法,然后在工具类里面增加响应处理即可,其他已经处理了的类,完全不需要去改动。
但是,也是优缺点,虽然简单的工厂设计模式解决了这个问题,但是这个模式只是解决了对象的创建问题,而且由于工厂本身包含了所有的收费方式,商场是有可能经常性的更改打折额度和返利额度,每次维护和扩展收费方式都要更改这个工厂类,以至于代码需要重新编译、部署,那这种处理方式就显得比较糟糕了。面对算法的时常变动,应该有更好的设计方法......
4. 策略模式
策略模式定义了算法家族,分别封装起来,让它们之间可以相互替换,此模式让算法的变化,不会影响到使用算法的客户。
商场收银时如何促销,用打折还是返利,其实都是一些算法,用工厂来生成算法对象,这没错,但是算法本身只是一种策略,最主要的是,这些算法本身是可以随时替换的,这就是变化点,而封装变化点是面向对象的一种很重要的思维方式。
接下来就是策略模式代码:
#import <Foundation/Foundation.h>
@interface ZYTotalPrices : NSObject
@property (nonatomic, assign) double singlePrices;
@property (nonatomic, assign) int number;
/**
* 如此,抽象类里面有一个方法声明,实现交给子类
*
*/
- (double)totalPrices;
@end #import "ZYTotalPrices.h"
@implementation ZYTotalPrices
@end
#import "ZYTotalPrices.h" @interface ZYPricesNoraml : ZYTotalPrices @end #import "ZYPricesNoraml.h" @implementation ZYPricesNoraml
/**
* 正常收费
*
*/
- (double)totalPrices
{
return self.singlePrices * self.number;
}
@end
#import "ZYTotalPrices.h" @interface ZYPricesDiscount : ZYTotalPrices
/**
* 打折率,为小数
*/
@property (nonatomic, assign) double moneyRebate; - (instancetype)initWithMoneyRebate:(double)moneyRebate;
@end #import "ZYPricesDiscount.h" @implementation ZYPricesDiscount - (instancetype)initWithMoneyRebate:(double)moneyRebate
{
if (self = [super init]) {
self.moneyRebate = moneyRebate;
}
return self;
} - (double)totalPrices
{
return self.number * self.singlePrices * self.moneyRebate;
}
@end
#import "ZYTotalPrices.h" @interface ZYPricesReturn : ZYTotalPrices
/**
* 返利条件
*/
@property (nonatomic, assign) double moneyCondition; /**
* 返利值
*/
@property (nonatomic, assign) double moneyReturn; - (instancetype)initWithMoneyCondition:(double)moneyCondition moneyReturn:(double)moneyReturn;
@end #import "ZYPricesReturn.h" @implementation ZYPricesReturn
- (instancetype)initWithMoneyCondition:(double)moneyCondition moneyReturn:(double)moneyReturn
{
if (self = [super init]) {
self.moneyCondition = moneyCondition;
self.moneyReturn = moneyReturn;
}
return self;
} - (double)totalPrices
{
double total = self.number * self.singlePrices;
return total - (int)(total / self.moneyCondition) * self.moneyReturn;
}
@end
#import <Foundation/Foundation.h>
@class ZYTotalPrices;
@interface ZYPricesContent : NSObject
@property (nonatomic, strong) ZYTotalPrices *totalPrice; /**
* 必须根据相应的itemStr字符串创建对应的算法对象
*
*/
- (instancetype)initWithItemStr:(NSString *)itemStr; - (double)getResult;
@end #import "ZYPricesContent.h"
#import "ZYTotalPrices.h"
#import "ZYPricesNoraml.h"
#import "ZYPricesDiscount.h"
#import "ZYPricesReturn.h" @implementation ZYPricesContent
- (instancetype)initWithItemStr:(NSString *)itemStr
{
if (self = [super init]) {
[self commitInit:itemStr];
}
return self;
} - (void)commitInit:(NSString *)itemStr
{
if ([itemStr isEqualToString:@"正常收费"]) {
self.totalPrice = [[ZYPricesNoraml alloc] init];
}
else if ([itemStr isEqualToString:@"满300返100"]) {
self.totalPrice = [[ZYPricesReturn alloc] initWithMoneyCondition:300 moneyReturn:100];
}
else {
self.totalPrice = [[ZYPricesDiscount alloc] initWithMoneyRebate:0.8];
}
} - (double)getResult
{
return [self.totalPrice totalPrices];
}
@end
仔细看这一次的代码,会发现,和上面用工厂调用的代码基本没什么变化,只是将工具类,改成了ZYPricesReturn类,这个类包含ZYTotalPrices类,持有ZYTotalPrices对象,还有就是原本在工具类里面判断的,具体调用哪个算法对象,也在ZYPricesReturn类里面实现了,可以说,ZYPricesReturn类就是为了ZYTotalPrices服务的。
来看看viewController里面的代码,就可以看出很明显的区别,也可以了解策略模式的好处在哪:
先是,调用工具类的viewController代码:
#import "ViewController.h"
#import "ZYPricesTool.h"
#import "ZYPricesNoraml.h"
#import "ZYPricesDiscount.h"
#import "ZYPricesReturn.h"
@interface ViewController () @end @implementation ViewController - (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib. ZYTotalPrices *totalPrices = [ZYPricesTool createTotalPricesWithItemStr:@"打八折"]; totalPrices.number = 5;
totalPrices.singlePrices = 6; NSLog(@"%lf",[totalPrices totalPrices]); }
@end
可以很明显的发现,这种方法实现的,viewController想要调用各种算法,那么就得包含各种算法的头文件,创建并持有各种算法对象。
那么,如果是策略模式实现的代码,viewController里面的代码是怎么样的呢:
#import "ViewController.h"
#import "ZYPricesContent.h"
#import "ZYTotalPrices.h"
@interface ViewController () @end @implementation ViewController - (void)viewDidLoad {
[super viewDidLoad]; ZYPricesContent *content = [[ZYPricesContent alloc] initWithItemStr:@"满300返100"];
content.totalPrice.number = 5;
content.totalPrice.singlePrices = 120; NSLog(@"%lf",[content getResult]);
}
@end
可以看到,viewController只需要认识ZYTotalPrices与ZYPricesContent这两个类即可,不必去认识各种算法类,大大降低了耦合度。
回头来反思下策略模式,策略模式是一种定义一系列算法的方法,从概念上看,所有这些算法完成的都是相同的工作,只是实现不同,他可以以相同的方式调用所有的算法,减少了各种算法类和使用各算法类之间的耦合。
策略模式就是用来封装算法的,但是在实际应用中,我们可以用它来封装几乎任何类型的规则,只要在分析过程中听到需要在不同时间应用不同的业务规则,就可以考虑使用策略模式处理这种变化的可能性。
设计模式之策略模式(iOS开发,代码用Objective-C展示)的更多相关文章
- 设计模式入门,策略模式,c++代码实现
// test01.cpp : Defines the entry point for the console application.////第一章,设计模式入门,策略模式#include &quo ...
- PHP设计模式之策略模式
前提: 在软件开发中也常常遇到类似的情况,实现某一个功能有多种算法或者策略,我们可以根据环境或者条件的不同选择不同的算法或者策略来完成该功能.如查 找.排序等,一种常用的方法是硬编码(Hard Cod ...
- [design-patterns]设计模式之一策略模式
设计模式 从今天开始开启设计模式专栏,我会系统的分析和总结每一个设计模式以及应用场景.那么首先,什么是设计模式呢,作为一个软件开发人员,程序人人都会写,但是写出一款逻辑清晰,扩展性强,可维护的程序就不 ...
- python设计模式之策略模式
每次看到项目中存在大量的if else代码时,都会心生一丝不安全感. 特别是产品给的需求需要添加或者更改一种if条件时,生怕会因为自己的疏忽而使代码天崩地裂,哈哈,本文的目的就是来解决这种不安全感的, ...
- JAVA 设计模式之策略模式
定义:定义一组算法,将每个算法都封装起来,并且使他们之间可以互换. 类型:行为类模式 策略模式是对算法的封装,把一系列的算法分别封装到对应的类中,并且这些类实现相同的接口,相互之间可以替换.在前面说过 ...
- 设计模式:策略模式(Strategy)
定 义:它定义了算法家族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化, 不会影响到使用算法的客户. 示例:商场收银系统,实现正常收费.满300返100.打8折.......等不同收费 ...
- JavaScript设计模式之策略模式(学习笔记)
在网上搜索“为什么MVC不是一种设计模式呢?”其中有解答:MVC其实是三个经典设计模式的演变:观察者模式(Observer).策略模式(Strategy).组合模式(Composite).所以我今天选 ...
- 乐在其中设计模式(C#) - 策略模式(Strategy Pattern)
原文:乐在其中设计模式(C#) - 策略模式(Strategy Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 策略模式(Strategy Pattern) 作者:webabc ...
- JavaScript设计模式之策略模式
所谓"条条道路通罗马",在现实中,为达到某种目的往往不是只有一种方法.比如挣钱养家:可以做点小生意,可以打分工,甚至还可以是偷.抢.赌等等各种手段.在程序语言设计中,也会遇到这种类 ...
随机推荐
- MVC 3.0学习笔记(自定义控件)
现有控件: 例如ASP.NET MVC框架包括以下设置标准的HTML控件(部分控件): Html.ActionLink() Html.BeginForm() Html.CheckBox() Html. ...
- Git 打标签(分布式版本控制系统)
前言 像其他版本控制系统(VCS)一样,Git 可以给历史中的某一个提交打上标签,以示重要.比较有代表性的是人们会使用这个功能来标记发布结点(v1.0 等等). 1.列出标签 在 Git 中列出已有的 ...
- 基于matplotlib的数据可视化 - 等高线 contour 与 contourf
contour 与contourf 是绘制等高线的利器. contour - 绘制等高线 contourf - 填充等高线 两个的返回值值是一样的(return values are the sam ...
- 'Agent XPs' component is turned off as part of the security configuration for this server
To turn on Agent XP's by running this script: sp_configure 'show advanced options', 1; Go RECONFIGUR ...
- memcache 与 redis 为web app 带来的性能提升
memcache 与 redis 为web app 带来的性能提升 参考: 1. http://www.cnblogs.com/ToDoToTry/p/3513688.html
- Redis 的安装与使用(单节点)
Redis 的安装与使用(单节点) 环境:CentOS 6.5 Redis 版本:redis-3.0 (考虑到Redis3.0 在集群和性能提升方面的特性,rc 版为正式版的候选版,而且 很快就出 ...
- 像网页开发一样调试ios程序
PonyDebugger https://github.com/square/PonyDebugger
- 使用inno setup 制作安装文件-demo1
; 脚本由 Inno Setup 脚本向导 生成! ; 有关创建 Inno Setup 脚本文件的详细资料请查阅帮助文档! #define MyAppName "查体管理系统" # ...
- Windows平台下tomcat+java的web程序持续占cpu问题调试
1.问题 Tomcat服务器跑了一段时间后,发现Tomcat进程占用的CPU资源在80%-100%间,加上其它的进程,整个服务器的CPU处理100%运行状态. 2.通过process explorer ...
- vim7.4版本在windows下的配置文件及所在位置
1.vim在windows下默认首先会查找"_vimrc"文件,如果没有则会找".vimrc".造成这个原因是windows早期不支持以点开头的文件及目录.2. ...