iOS开发-均衡代码职责

码农小明的门类

在开头在此之前,仍旧强烈推荐推荐《重构-改善既有代码的设计》这本书,一本好书或者好著作理所应当让您每一回观赏时都能暴发不同的感觉到。

正常的话,造成你代码笨重的最大杀手是重新的代码,例如曾经笔者看过如此一张界面图以及逻辑代码:

图片 1

@interface XXXViewController

@property (weak, nonatomic) IBOutlet UIButton * rule1;
@property (weak, nonatomic) IBOutlet UIButton * rule2;
@property (weak, nonatomic) IBOutlet UIButton * rule3;
@property (weak, nonatomic) IBOutlet UIButton * rule4;

@end

@implementation XXXViewController

- (IBAction)actionToClickRule1: (id)sender {
    [_rule1 setSelected: YES];
    [_rule2 setSelected: NO];
    [_rule3 setSelected: NO];
    [_rule4 setSelected: NO];
}

- (IBAction)actionToClickRule2: (id)sender {
    [_rule1 setSelected: NO];
    [_rule2 setSelected: YES];
    [_rule3 setSelected: NO];
    [_rule4 setSelected: NO];
}

- (IBAction)actionToClickRule1: (id)sender {
    [_rule1 setSelected: NO];
    [_rule2 setSelected: NO];
    [_rule3 setSelected: YES];
    [_rule4 setSelected: NO];
}

- (IBAction)actionToClickRule1: (id)sender {
    [_rule1 setSelected: NO];
    [_rule2 setSelected: NO];
    [_rule3 setSelected: NO];
    [_rule4 setSelected: YES];
}

@end

别急着嘲讽那样的代码,曾经的大家也写过类似的代码。这就是最直接粗浅的再度代码,所有的重新代码都和位置存在一样的病痛:亢长、无意义、占用了大量的半空中。实际上,这几个重新的代码总是分散在两个类当中,积少成多让我们的代码变得笨重。由此,在议论你的系列是否需要鼎新架构从前,先弄领悟你是否需要排除这多少个垃圾。

举个例证,小明开发的一款面向B端的利用中允许商户添加促销活动,包括起头日期和得了日期:

@interface Promotion: NSObject

+ (instancetype)currentPromotion;

@property (readonly, nonatomic) CGFloat discount;
@property (readonly, nonatomic) NSDate * start;
@property (readonly, nonatomic) NSDate * end;

@end

出于商户同一时间只会存在一个优惠活动,小明把移动写成了单例,然后其它模块通过拿到活动单例来总括折后价格:

//  module A
Promotion * promotion = [Promotion currentPromotion];
NSDate * now = [NSDate date];
CGFloat discountAmount = _order.amount;
if ([now timeIntervalSinceDate: promotion.start] > 0 && [now timeIntervalSinceDate: promotion.end] < 0) {
    discountAmount *= promotion.discount;
}

//  module B
Promotion * promotion = [Promotion currentPromotion];
NSDate * now = [NSDate date];
if ([now timeIntervalSinceDate: promotion.start] > 0 && [now timeIntervalSinceDate: promotion.end] < 0) {
    [_cycleDisplayView display: @"全场限时%g折", promotion.discount*10];
}

//  module C
...

小明在付出形成后优化代码时发现了五个模块存在这么的双重代码,于是他写了一个NSDate的扩大来简化了这段代码,顺便还添加了一个平安监测:

@implementation NSDate (convenience)

- (BOOL)betweenFront: (NSDate *)front andBehind: (NSDate *)behind {
    if (!front || !behind) { return NO; }
    return ([self timeIntervalSinceDate: front] > 0 && [self timeIntervalSinceDate: behind] < 0);
}

@end

//  module A
Promotion * promotion = [Promotion currentPromotion];
NSDate * now = [NSDate date];
CGFloat discountAmount = _order.amount;
if ([now betweenFront: promotion.start andBehind: promotion.end]) {
    discountAmount *= promotion.discount;
}

//  module B
Promotion * promotion = [Promotion currentPromotion];
NSDate * now = [NSDate date];
if ([now betweenFront: promotion.start andBehind: promotion.end]) {
    [_cycleDisplayView display: @"全场限时%g折", promotion.discount*10];
}

过了一段时间,产品找到小明说:小明啊,商户反映说唯有一个打折活动是不够的,他们需要存在五个不同的位移。小明一想,那么就收回Promotion的单例属性,扩展一个管制单例:

@interface PromotionManager: NSObject

@property (readonly, nonatomic) NSArray<Promotion *> * promotions

+ (instancetype)sharedManager;
- (void)requestPromotionsWithComplete: (void(^)(PromotionManager * manager))complete;

@end

//  module A
- (void)viewDidLoad {
    PromotionManager * manager = [PromotionManager sharedManager];
    if (manager.promotions) {
        [manager requestPromotionsWithComplete: ^(PromotionManager * manager) {
            _promotions = manager.promotions;
            [self calculateOrder];
        }
    } else {
        _promotions = manager.promotions;
        [self calculateOrder];
    }
}

- (void)calculateOrder {
    CGFloat orderAmount = _order.amount;
    for (Promotion * promotion in _promotions) {
        if ([[NSDate date] betweenFront: promotion.start andBehind: promotion.end]) {
            orderAmount *= promotion.discount;
        }
    }
}

乘势生活一每一日病逝,产品指出的需要也尤为多。有一天,产品说应该让经纪人可以无限制开关促销活动,于是Promotion多了一个isActived是否激活的性能。其他模块的判断除了判断时间还多了判断是否启动了运动。再后来,还添加了一个synchronize性能判断是否足以与其他运动同时总计判断。如今产品告诉小明活动前几天不光局限于折扣,还新增了一定促销,以及满额促销,于是代码变成了上面这样:

@interface Promotion: NSObject

@property (assign, nonatomic) BOOL isActived;
@property (assign, nonatomic) BOOL synchronize;
@property (assign, nonatomic) CGFloat discount;
@property (assign, nonatomic) CGFloat discountCondition;
@property (assign, nonatomic) DiscountType discountType;
@property (assign, nonatomic) PromotionType promotionType;

@property (readonly, nonatomic) NSDate * start;
@property (readonly, nonatomic) NSDate * end;

@end

//  module A
- (void)viewDidLoad {
    PromotionManager * manager = [PromotionManager sharedManager];
    if (manager.promotions) {
        [manager requestPromotionsWithComplete: ^(PromotionManager * manager) {
            _promotions = manager.promotions;
            [self calculateOrder];
        }
    } else {
        _promotions = manager.promotions;
        [self calculateOrder];
    }
}

- (void)calculateOrder {
    CGFloat orderAmount = _order.amount;
    NSMutableArray * fullPromotions = @[].mutableCopy;
    NSMutableArray * discountPromotions = @[].mutableCopy;
    for (Promotion p in _promotions) {
        if (p.isActived && [[NSDate date] betweenFront: p.start andBehind: p.end]) {
            if (p.promotionType == PromotionTypeFullPromotion) {
                [fullPromotions addObject: p];
            } else if (p.promotionType == PromotionTypeDiscount) {
                [discountPromotions addObject: p];
            }
        }
    }

    Promotion * syncPromotion = nil;
    Promotion * singlePromotion = nil;
    for (Promotion * p in fullPromotions) {
        if (p.synchronize) {
            if (p.discountCondition != 0) {
                if (p.discountCondition > syncPromotion.discountCondition) {
                    syncPromotion = p;
                }
            } else {
                if (p.discount > syncPromotion.discount) {
                    syncPromotion = p;
                }
            }
        } else {
            if (p.discountCondition != 0) {
                if (p.discountCondition > singlePromotion.discountCondition) {
                    singlePromotion = p;
                }
            } else {
                if (p.discount > singlePromotion.discount) {
                    singlePromotion = p;
                }
            }
        }
    }
    //  find discount promotions
    ......
}

这时模块获取让利活动消息的代价已经变得那么些的高昂,一堆亢长的代码,重复度高。这时候小明的同事对他说,大家立异一下架构吧,通过ViewModel把这部分的代码从控制器分离出来。其实这时候ViewModel的做法跟上边小明直接扩张NSDate的目标是一律的,在这一个时候ViewModel差一点无作为,基本所有逻辑都在控制器中持续地撑胖它。小明认真考虑,完完全全将代码观望后,告诉同事现在最大的原因在于代码职责混乱,并不可能很好的离别到VC的模块中,解决的点子应该是从逻辑分工入手。

首先,小明发现Promotion自家除了存储活动音信,没有展开此外的逻辑操作。而控制器中判断活动是否有效以及折扣金额总计的业务理可以由Promotion来完成:

@interface Promotion: NSObject

- (BOOL)isEffective;
- (BOOL)isWorking;
- (CGFloat)discountAmount: (CGFloat)amount;

@end

@implementation Promotion

- (BOOL)isEffective {
    return [[NSDate date] betweenFront: _start andBehind: _end];
}

- (BOOL)isWorking {
    return ( [self isEffective] && _isActived );
}

- (CGFloat)discountAmount: (CGFloat)amount {
    if ([self isWorking]) {
        if (_promotionType == PromotionTypeDiscount) {
            return [self calculateDiscount: amount];
        } else {
            if (amount < _discountCondition) { return amount; }
            return [self calculateDiscount: amount];
        }
    }
    return amount;
}

#pragma mark - Private
- (CGFloat)calculateDiscount: (CGFloat)amount {
    if (_discountType == DiscountTypeCoupon) {
        return amount - _discount;
    } else {
        return amount * _discount;
    }
}

@end

除却,小明发现原先打包的运动管理类PromotionManager自家涉及了网络请求和数据管理六个工作,因而需要将内部一个事情分离出来。于是网络请求封装成PromotionRequest,另一方面原有的数量管理只有获取数据的效应,因而扩张增删改以及对活动举行开头筛选的意义:

#pragma mark -  PromotionManager.h
@class PromotionManager;
typeof void(^PromotionRequestComplete)(PromotionManager * manager);

@interface PromotionRequest: NSObject

+ (void)requestPromotionsWithComplete: (PromotionRequestComplete)complete;
+ (void)insertPromotion: (Promotion *)promotion withComplete: (PromotionRequestComplete)complete;
+ (void)updatePromotion: (Promotion *)promotion withComplete: (PromotionRequestComplete)complete;
+ (void)deletePromotion: (Promotion *)promotion withComplete: (PromotionRequestComplete)complete;

@end


@interface PromotionManager: NSObject

+ (instancetype)sharedManager;

- (NSArray<Promotion *> *)workingPromotions;
- (NSArray<Promotion *> *)effectivePromotions;
- (NSArray<Promotion *> *)fullPromotions;
- (NSArray<Promotion *> *)discountPromotions;

- (void)insertPromotion: (Promotion *)promotion;
- (void)updatePromotion: (Promotion *)promotion;
- (void)deletePromotion: (Promotion *)promotion;

@end



#pragma mark -  PromotionManager.m
@interface PromotionManager ()

@property (nonatomic, strong) NSArray<Promotion *> * promotions;

@end

@implementation PromotionManager

+ (instancetype)sharedManager { ... }

- (NSArray<Promotion *> *)fullPromotions {
    return [self filterPromotionsWithType: PromotionTypeFullPromote];
}

- (NSArray<Promotion *> *)discountPromotions {
    return [self filterPromotionsWithType: PromotionDiscountPromote];
}

- (NSArray<Promotion *> *)workingPromotions {
    return _promotions.filter(^BOOL(Promotion * p) {
        return (p.isWorking);
    });
}

- (NSArray<Promotion *> *)effectivePromotions {
    return _promotions.filter(^BOOL(Promotion * p) {
        return (p.isEffective);
    });
}

- (NSArray<Promotion *> *)filterPromotionsWithType: (PromotionType)type {
    return [self workingPromotions].filter(^BOOL(Promotion * p) {
        return (p.promotionType == type);
    });
}

- (void)insertPromotion: (Promotion *)promotion { 
    if ([_promotions containsObject: promotion]) {
        [PromotionRequest updatePromotion: promotion withComplete: nil];
    } else {
        [PromotionRequest insertPromotion: promotion withComplete: nil];
    }
 }

- (void)updatePromotion: (Promotion *)promotion { 
    if ([_promotions containsObject: promotion]) {
        [PromotionRequest updatePromotion: promotion withComplete: nil];
    }
 }

- (void)deletePromotion: (Promotion *)promotion { 
    if ([_promotions containsObject: promotion]) {
        [PromotionRequest deletePromotion: promotion withComplete: nil];
    }
}

- (void)obtainPromotionsFromJSON: (id)JSON { ... }

@end

说到底,小明发现其他模块在搜寻最打折活动的逻辑代码十分的多,另外是因为存在满额促销和平日优惠两种运动,进一步加大了代码量。由此小明新建了一个总括类PromotionCalculator用来形成搜索最优活动和统计最优价格的接口:

@interface PromotionCalculator: NSObject

+ (CGFloat)calculateAmount: (CGFloat)amount;
+ (Promotion *)bestFullPromotion: (CGFloat)amount;
+ (Promotion *)bestDiscountPromotion: (CGFloat)amount;

@end

@implementation PromotionCalculator

+ (CGFloat)calculateAmount: (CGFloat)amount {
    Promotion * bestFullPromotion = [self bestFullPromotion: amount];
    Promotion * bestDiscountPromotion = [self bestDiscountPromotion: amount];
    if (bestFullPromotion.synchronize && bestDiscountPromotion.synchronize) {
        return [bestFullPromotion discountAmount: [bestDiscountPromotion discountAmount: amount]];
    } else {
        return MAX([bestDiscountPromotion discountAmount: amount], [bestFullPromotion discountAmount: amount]);
    }
}

+ (Promotion *)bestFullPromotion: (CGFloat)amount {
    PromotionManager * manager = [PromotionManager sharedManager];
    return [self bestPromotionInPromotions: [manager fullPromotions] amount: amount];
}

+ (Promotion *)bestDiscountPromotion: (CGFloat)amount {
    PromotionManager * manager = [PromotionManager sharedManager];
    return [self bestPromotionInPromotions: [manager discountPromotions] amount: amount];
}  

+ (Promotion *)bestPromotionInPromotions: (NSArray *)promotions amount: (CGFloat)amount {
    CGFloat discount = amount;
    Promotion * best = nil;
    for (Promotion * promotion in promotions) {
        CGFloat tmp = [promotion discountAmount: amount];
        if (tmp < discount) {
            discount = tmp;
            best = promotion;
        }
    }
    return best;
}

@end

当这个代码逻辑被小明分散到大街小巷之后,小明惊讶的意识任何模块在举办测算时多余几行代码而已:

- (void)viewDidLoad {
    [PromotionRequest requestPromotionsWithComplete: ^(PromotionManager * manager) {
        _discountAmount = [PromotionCalculator calculateAmount: _order.amount];
    }];
}

此时代码职责的布局图,小明成功的动态平衡了不同组件之间的代码职责,制止了转移项目原架构带来的高风险以及不必要的工作:

图片 2

第四,放下父母的体面。希望孩子成为一个宏观的男女,究竟是“为了她好”,仍然为了自己的颜面?而实在,完美的儿女又是否存在?我们的孩子,他是一个举世无双的男女,不是为了老人的面目而活。面子这多少个砍,说来容易,做到科学,且做且战胜吧。

尾语

这是第二篇讲MVC的著作,如故要告诉我们的是MVC确实存在着欠缺,这多少个毛病会在档次变得很大的时候显流露来(笔者没有支付过大型项目标弱鸡),假设您的档次布局分段做的够用完善的话,那么该立异更换架构的时候就毫无犹豫。但相对要牢记,假如单单是因为重新了太多的不行代码,又或者是逻辑全部塞到控制器中,那么更换架构无非是将放弃物再一次分散罢了。

关注iOS开发得到笔者更新动态
转载请讲明地址以及作者

自我后日看了一句话,感受颇深,中国父母和儿女之间缺的不是关联,而是缺乏把相互当作独立的私房来强调。这句话,是因为网上爆红的两封父子对话的家书,有网友爆发的慨叹。百般滋味在心里,你的父小姨是怎么说服你去考公务员的?他们是怎么着告诉您读书无法恋爱,毕业顿时结婚的?

前言

作品的题目有点绕口,不过想了半天,想不到更好的标题了。本文的降生有一对佳绩要归于iOS应用现状分析,标题也是根源原文中的“能把代码职责均衡的分割到不同的功用类里”。假设您看过自己的作品,就会发觉自家是一个MVC中心开发的人。这是因为支付的品类连续算不上大品类,在客观的代码职责分工后项目能保持优秀的动静,就从未有过利用到另外架构开发过项目(假使您的气象跟笔者差不多,就算不适用其他架构形式,你也应有自己读书)

图片 3

OK,简短来说,在很早在此以前我就有写这样一篇小说的想法,大致是在这儿面试很多iOS开发者的时候这样的对话萌生的心理,下面的对话是经过作者总计的,切勿对号落座:

Q: 你在项目中应用了MVVM的架构结构,能说说怎么使用的是这种结构吧?

A:
这是因为我们的品种在支付中控制器的代码越来越多,超越了一千行,然后觉得这样控制器的任务太多,就采取一个个ViewModel把这多少个任务分离出来

Q: 能说说你们控制器的职责吗?或者有源码可以参照一下呢?

面试者拿出电脑突显源码

说到底的结果就是,笔者不觉得面试者需要接纳到MVVM来鼎新他们的架构,这里当然是不同了。由于对方代码职责的不客观分工导致了ViewModel层几乎没有工作逻辑,从而致使了控制器的失衡,变得笨重。在这种情状下虽然她运用了ViewModel将控制器的代码分离了出去,充其量只是将垃圾挪到另一个地方罢了。我在MVC架构杂谈中提到过自家对MVC四个模块的职责认识,当您想将MVC改进成MVX的任何协会时,应抢先思考自己的代码职责是不是曾经均衡了。

其三,不参照别人家的孩子。为啥其他小朋友都经历了一样的历程所有人都出演了,唯独你做不到?我们也思考刻钟候老人跟大家说别人家男女塞尔维亚语考100分的时候,你立时是怎么的感触?孩子觉得您不认同我的竭力,为啥自己数学考了100分你看不见?我跑步跑得急忙你看不见?我作的大力都是白费的。

业务是这样的,三岁的儿女深夜出去活动,上午没怎么睡觉,早上拭目以待演出的日子足有多少个钟头,而且现场相比较拥挤闷热,最后出台的时候就不肯上去了。二伯大发雷霆,认为孩子胆小、懦弱。

其次,大家可否不给孩子贴标签。胆小、懦弱、怕事、内向、娇纵……我们的儿女如玉璞,他将会成为咋样的人是靠你一手一脚带出来的,而非三岁定八十。况且,他一岁的时候怕别人,两岁的时候只说不,三岁的时候有呼声……这么些都不是贴上标签的说辞,而是什么陪伴她改成她协调。

第五,言传不及身教。最终,落实到方法的问题,老话,你期望子女变成如何的人,首先,自己得是怎么的人。应该感谢孩子,让您变成更好的投机。因为更好的你,才能塑造出更好的儿女。

自家有一个姨妈群,多少个闺蜜都已为人母,平常在其间商量育儿经验,吐槽各个家庭教育问题。今儿个闺蜜外孙子因为六一演出临阵退缩,晌午在家里引发了一场有关家庭教育问题的大讨(zhan)论(zheng)。

率先,大家把他当作一个人吧。不管她有多么美妙或者多么普通,他都有长处缺点,更有心思和感触。他不想出演,是因为不痛快?依然不耐烦?他当即是怎么想的?有什么的感受?尽管孩子能够直接愿意向老人倾诉心底真正的感触,是不是能更好地找到解决问题的方法吧?我想是不是足以尝尝先对她的感触表示领悟,听他倾诉为啥做那么些决定,然后再帮忙她随同她伙同化解将来看似的题目?

未经授权,禁止转载。

发表评论

电子邮件地址不会被公开。 必填项已用*标注