一次单例模式的代码重构

Posted 攻城狮小明的技术历程

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一次单例模式的代码重构相关的知识,希望对你有一定的参考价值。


不要滥用设计模式。


最近在做代码剥离重构, 有一个基础库A,作为面向业务开发者的接口库,随着业务的发展,很多内容都往里面塞。虽然,中间每次添加都或多或少考虑了解耦,抽象了很多协议接口,但架不住常年累月的堆积啊。

熟悉设计模式的人,对单例模式一定不陌生吧。一次创建、处处可用。也不用担心用错。

这里讲到的是一个解析配置文件的单例, 作为配置文件的管理类, 内部会包含多个不同文件的管理对象,然后对外提供统一的接口。但是这些管理对象呢,又存在相互依赖的关系。然后呢, 有部分文件的管理对象就也设计成了单例, 又有一部分的文件的管理对象却是普通对象。设计成单例的文件管理对象,基本上是被其它管理对象或者其它类依赖了的。库中的其它类有的依赖管理对象,有的依赖了具体文件的管理对象。总之,就是有点乱。

初始类图关系大致如下:


现在,有一个需求,进入每个页面后,页面的配置更改不能影响全局的配置。然后,我就很崩溃了,本来呢, 快照可以是管理对象的一个实例就行了, 每次进页面创建一个快照管理对象,配置更新全部在快照管理对象上处理就行了。可是,好多单例啊, 一旦修改,全局都生效了。好吧, 花点时间把管理理一下, 然后有如下的新类图:


图中的#号表示这些属性对内部的类暴露, OC实现就是用扩展,然后内部类可以包含扩展的头文件。对外业务暴露的接口不包含扩展的接口和属性。内部其它类只依赖管理对象, 然后把具体文件的管理对象的单例干掉。在管理对象中,生成每个文件的管理对象实例。部分文件的管理对象依赖管理对象, 不再依赖具体的文件管理对象。这里, 我还把管理对象中属性的初始化通过懒加载实现, 为了保证线程安全, 在初始化的地方加锁, 由于存在文件管理对象依赖文件管理对象的情况, 这个锁还需要可重入的,也就是递归锁。实现代码如下:


// ConfigManager.m
- (instancetype)init {
self = [super init];
if (self) {
_getterLock = [[NSRecursiveLock alloc] init]; // 处理懒加载,初始化 多线程互斥
// ...
}

return self;
}

- (FileAConfigManager *)aConfigManager {

if (!_aConfigManager) {
[_getterLock lock]; // 空值加锁
if (!_aConfigManager) { // 二次判断
_aConfigManager = [[FileAConfigManager alloc] init];
}
[_getterLock unlock];
}

return _aConfigManager;
}

- (FileBConfigManager *)bConfigManager {

if (!_bConfigManager) {
[_getterLock lock];// 空值加锁
if (!_bConfigManager) {// 二次判断
_bConfigManager = [[FileBConfigManager alloc] init];
}
[_getterLock unlock];
}

return _bConfigManager;
}
...



// FileAConfigManager.m



- (void)method {


/*

* 旧代码
* id<FileConfigManagerInterface> bManager = [FileBConfigManager shareInstance]];
*/

// 新代码通过管理对象访问文件对象

id<FileConfigManagerInterface> bManager = [[ConfigManager shareInstance] bConfigManager];

...

}



好了, 进入页面,创建一个PageConfigManager, 初始化时都是一样的。在进入页面后,更改的也只是操作当前PageConfigManager,不会影响到全局。

这里, 还好原先设计的逻辑比较清晰, 而且 只有内部的类使用到了具体的文件管理对象。对外接口是统一的,改起来还可以接受吧。也许之前场景没有考虑到后续的配置独立的需求,为了方便,使用了单例模式。随着需求的演进, 需要不断的对代码进行重构,以适应新的需求。

如果之前写代码的时候,不贪图这点方便,直接一步到位, 可能会是更好的设计。后面,只需要直接用,或者继承一下就好了。

切记,设计模式不可滥用,但是好的设计必须要用到设计模式。重构需要捋清楚各个类的关系,对外接口清晰,类职责单一。


今天是2019.90.30, 明天就是国庆了, 中华人民共和国建国70周年, 祖国生日快乐!!


以上是关于一次单例模式的代码重构的主要内容,如果未能解决你的问题,请参考以下文章

双重检查锁实现单例(java)

单例模式的“诱惑”

设计模式之单例模式

如何重构这个 Java 代码片段

PHP单例模式简记

JS单例模式