设计模式之ios实现我爱编程

设计模式原则之单一职责原则

2018-04-04  本文已影响21人  充满活力的早晨

定义

There should never be more than one reason for a class to change.

应该有且仅有一个原因引起类的变更。


优点

降低类的复杂性

每个类实现单一职责,并且单一职责都有清楚明确的定义,复杂性当然降低。

提高可读性 

类的复杂性降低了,当然提高了可读性了。

提高可维护性 

类的复杂性降低,可读性好,当然好维护。

易于测试

测试单一目标的类只需要很少的测试类。让“用测试替代文档” “self documentation by tests”变得更加容易

易于调试

在一个单一职责类找到问题是一件更容易的事情。

变更引起的风险降低,变更是必不可少的,如果接口的单一职责做的好,一个接口修改只对相应的实现类有影响,对其它的接口没有影响,这对系统的扩展性,维护性都是有好处的。


类的单一职责原则

一般一个对象可以分为属性行为二部分,所以在类的设计时,我们一般把对象的属性抽象成一个BO(Business Object,业务对象),把对象的行为抽象成一个Biz(Business Logic,业务逻辑)


产生原因

没有任何的程序设计人员不清楚应该写出高内聚低耦合的程序,但是很多耦合常常发生在不经意之间,其原因就是:职责扩散:因为某种原因,某一职责被分化为颗粒度更细的多个职责了

解决办法

遵守单一职责原则,将不同的职责封装到不同的类或模块中。


场景模拟

我们需要创作一个论坛,需要发布主题和回帖功能

场景模拟UML 图

场景模拟UML图

简单代码

@protocol Thread<NSObject>

-(void)addReplayMessage;

@end

#import "Thread.h"

@protocol Forum<NSObject>

-(void)addThread:(id<Thread>)thread;

-(void)addReplayMessage;

@end

#import "Forum.h"

@interface ForumObject : NSObject<Forum>

@end

#import "ForumObject.h"

@interface ForumObject()

@property (nonatomic,strong) id<Thread>thread;

@end@implementation ForumObject

-(void)addThread:(id<Thread>)thread{

    self.thread = thread;

}

-(void)addReplayMessage{

    [self.thread addReplayMessage];

}

@end

#import "Thread.h"

@interface ThreadObject : NSObject<Thread>

@property (nonatomic,strong) NSString *name;

@end

#import "ThreadObject.h"

@implementation ThreadObject

-(void)addReplayMessage{

    NSLog(@"%@ 回复信息",self.name);

}

@end

测试代码

id<Forum> forumObject = [ForumObject new];

ThreadObject * threadobject = [ThreadObject new];

threadobject.name =@"单一职责原则";

[forumObject addThread:threadobject];

[forumObject addReplayMessage];

测试结果

2018-04-04 14:39:55.425610+0800 设计模式原则[90446:6274362] 单一职责原则 回复信息


分析

我们都知道一个论坛的结构一般是

forum------->Thread----------->Message

一个论坛Forum中有多个Thread ,一个Thread 有多个回帖和跟帖。

根据这个层次结构,forum 增加回帖的功能有点太宽了,这个功能应该属于Thread的


代码重构

#import "Thread.h"

@protocol ForumNew<NSObject>

-(void)addThread:(id<NSObject>)thread;

@end

#import "ForumNew.h"

@interface ForumNewObject : NSObject<ForumNew>

@end

#import "ForumNewObject.h"

@interface ForumNewObject()

@property (nonatomic,strong) id<Thread>thread;

@end

@implementation ForumNewObject-

(void)addThread:(id<Thread>)thread{

    self.thread = thread;

}

@end

测试代码

 id<ForumNew> forumObject= [ForumNewObject new];

   ThreadObject * threadobject = [ThreadObject new];

    threadobject.name =@"单一职责原则";

    [forumObject addThread:threadobject];

    [threadobject addReplayMessage];

结果

2018-04-04 14:50:13.955267+0800 设计模式原则[93059:6286368] 单一职责原则 回复信息

其实重构代码就是讲回帖的功能从forum拿到了Thread中,代码很简单,就是需要体会下。

上述问题产生的原因是因为当时我们把论坛的Thread和Message都当做论坛的一个功能。由于后期业务变更,导致Thread  和Message 需要划分为更细致。


如何识别SRP被破坏?

类有太多依赖

类的构造器有太多参数,意味着测试有太多依赖,需要制造mock太多测试输入参数,通常意味着已经破坏SRP了。

方法有太多参数

类似类的构造器,方法参数意味着依赖。

测试类变得复杂

如果测试有太多变量,意味着这个类有太多职责。

类或方法太长

如果方法太长,意味着内容太多,职责过多。

一个类不超过 200-250

描述性名称

如果你需要描述你的类 方法或包,比如使用"xxx和xxx"这种语句,意味着可能破坏了SRP.

低聚合Cohesion的类

聚合Cohesion是一个很重要的概念,虽然聚合是有关结构概念,但是聚合和SRP非常相关,如前面论坛案例,如果一个类不代表一个高聚合,意味着低凝聚low Cohesion,它就可能意味破坏SRP。一个低凝聚的特点:

一个类有两个字段,其中一个字段被一些方法使用;另外一个字段被其他方法使用。

在一个地方改动影响另外一个地方

如果在一个代码地方加入新功能或只是简单重构,却影响了其他不相关的地方,意味着这个地方代码可能破坏了SRP.

猎枪效果Shotgun Effect

如果一个小的改变引起一发动全身,这意味SRP被破坏了。

不能够封装模块

比如使用Spring框架,你使用@Configuration or XML 配置,如果你不能在一个配置中封装一个Bean。意味着它有太多职责,Spring配置应该隐藏内部bean,暴露最少接口,如果你因为多个原因需要改变Spring配置,可能破坏了SRP.

借鉴博客

单一职责原则(SRP)

六大设计原则之单一职责原则

源代码地址

下一篇博客

设计原则之迪米特原则

上一篇 下一篇

猜你喜欢

热点阅读