相关阅读:
JAVA编程思想(一)通过依赖注入增加扩展性
JAVA编程思想(二)如何面向接口编程
JAVA编程思想(三)去掉别扭的if,自注册策略模式优雅满足开闭原则
JAVA编程思想(四)Builder模式经典范式以及和工厂模式如何选?
Java编程思想(五)事件通知模式解耦过程
Java编程思想(六)事件通知模式解耦过程
Java编程思想(七)使用组合和继承的场景
JAVA基础(一)简单、透彻理解内部类和静态内部类
JAVA基础(二)内存优化-使用Java引用做缓存
JAVA基础(三)ClassLoader实现热加载
JAVA基础(四)枚举(enum)和常量定义,工厂类使用对比
JAVA基础(五)函数式接口-复用,解耦之利刃
HikariPool源码(二)设计思想借鉴
【极客源码】JetCache源码(一)开篇
【极客源码】JetCache源码(二)顶层视图
人在职场(一)IT大厂生存法则
1. 起因
今天看到RocketMQ源码的KVConfigManager类,其中的部分代码有重复,如下:
1.1 put方法
1.2 delete方法
红框内的代码是重复的(除了log日志打印的描述信息不同外),对于代码极简主义者,不由想把它优化掉。
2. 重构
2.1. 初次重构想法
代码中多个方法有固定部分,又有变化部分,让人首先想到了模板方法模式:
看起来真的很接近,但模板方法模式是”多个子类“继承一个父类,每个子类可以有不同的行为(可变部分),而当前的场景是”一个类中的多个方法“需要用到模板内容,并且不可能为了适配模板方法模式,将这些方法拆到多个类中。
2.2. 二次重构
从代码中可以看到,可变部分也是一段代码,如果这段代码能动态传入,将固定部分和动态部分组成一个模板方法就是不是就可以了,而java支持函数式接口,因此这个想法变得可能,由此得到如下类结构图:
下面看代码。
2.2.1. 重构前
为了方便调试和说明,对代码做了简化:
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* @ClassName KVConfigManager
* @Description
* @Author 铿然一叶
* @Date 2021/1/24 13:39
* @Version 1.0
* 掘金:https://juejin.im/user/5d48557d6fb9a06ae071e9b0
**/
public class KVConfigManager {
private final ReadWriteLock lock = new ReentrantReadWriteLock();
private Map<String, String> map = new HashMap<>();
public static void main(String[] args) {
KVConfigManager manager = new KVConfigManager();
String key = "server";
manager.put(key, "10.25.33.45");
System.out.println(manager.get(key));
manager.delete(key);
System.out.println(manager.get(key));
}
public void put(String key, String value) {
try {
this.lock.writeLock().lockInterruptibly();
try {
map.put(key, value);
} finally {
this.lock.writeLock().unlock();
}
} catch (InterruptedException e) {
System.out.println("putKVConfig InterruptedException" + e);
}
}
public void delete(String key) {
try {
this.lock.writeLock().lockInterruptibly();
try {
map.remove(key);
} finally {
this.lock.writeLock().unlock();
}
} catch (InterruptedException e) {
System.out.println("deleteKVConfig InterruptedException" + e);
}
}
public String get(String key) {
try {
this.lock.readLock().lockInterruptibly();
try {
return map.remove(key);
} finally {
this.lock.readLock().unlock();
}
} catch (InterruptedException e) {
System.out.println("getKVConfig InterruptedException" + e);
}
return null;
}
}
2.2.2. 重构后
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* @ClassName CleanCodeKVConfigManager
* @Description
* @Author 铿然一叶
* @Date 2021/1/24 13:51
* @Version 1.0
* 掘金:https://juejin.im/user/5d48557d6fb9a06ae071e9b0
**/
public class CleanCodeKVConfigManager {
private final ReadWriteLock lock = new ReentrantReadWriteLock();
private Map<String, String> map = new HashMap<>();
public static void main(String[] args) {
CleanCodeKVConfigManager manager = new CleanCodeKVConfigManager();
String key = "server";
manager.put(key, "10.25.33.45");
System.out.println(manager.get(key));
manager.delete(key);
System.out.println(manager.get(key));
}
public void put(String key, String value) {
writeTemplate((args)->{ map.put(args[0], args[1]); }, key, value);
}
public void delete(String key) {
writeTemplate((args)->{ map.remove(args); }, key);
}
public String get(String key) {
try {
this.lock.readLock().lockInterruptibly();
try {
return map.remove(key);
} finally {
this.lock.readLock().unlock();
}
} catch (InterruptedException e) {
System.out.println("getKVConfig InterruptedException" + e);
}
return null;
}
private void writeTemplate(WriteFun writeFun, String... args) {
try {
this.lock.writeLock().lockInterruptibly();
try {
writeFun.exec(args);
} finally {
this.lock.writeLock().unlock();
}
} catch (InterruptedException e) {
System.out.println("deleteKVConfig InterruptedException" + e);
}
}
private interface WriteFun {
void exec(String... args);
}
}
2.2.3. 遗留部分
- 模板方法里的打印描述是固定的,这个增加一个参数传入就好
System.out.println("deleteKVConfig InterruptedException" + e);
- 读的部分是否也要封装?例如增加一个参数表达是读还是写,在模板方法中判断此参数做不同处理。
这个可以结合实际情况来,目的是减少重复代码和使代码变得简洁,能达到此目的就重构,否则可先保留。
3. 总结
1. 对于模板式的重复代码,除了想到使用模板方法设计模式重构以外,还可以使用方法级模板方法重构。
2. 减少重复代码的目的是只维护一份,有变化时只需要维护一处,如果维护多处则可能由于疏忽导致某处遗漏,最终行为不一致,因此减少重复代码的目的不仅仅是减少代码行,不要觉得没有减少代码行就没有必要重构。
3. 本次重构仅仅是个例子,以了解方法级模板方法的应用场景,什么场景需要重构看企业文化,有的企业可能会觉得例子中的代码很稳定,将来不会变化,不强制要求重构。
end.