趋向Factory的重构
笔记版本:1.0
作者:BlankFox(CSDN主页)
归档:重构与模式
写在前面:
ps: 重构是针对现有代码的重新构建,使得其更易于理解、扩展以及修改,在重构的过程中不能凭感觉修改代码,这会导致很多问题——重构后代码不能运行、代码更复杂、代码产生新的需要重构的部分。因此,代码重构需要一些发现问题的方法指导,以及规定一些大致步骤,循序渐进,逐步测试。
ps:在既有代码中,一定存在很多创建对象的内容,优秀的代码会很清晰的告诉代码读者——创建了什么对象,而不是让读者去了解其中的构造函数细节——如果代码中出现了许多同名构造函数-使用重构以创建方法封装、如果出现了多个构建方法使得原类的职责不清晰-使用重构提取创建方法包装成简单工厂、出现了类似函数但其中有不同的创建对象方法-使用重构趋向工厂方法
趋向Factory的重构
一、代码问题
首先确定问题,才能知道哪个地方可以优化
错 误 示 例 . 1 — — 太 多 的 同 名 构 造 函 数 \color{red}{错误示例.1——太多的同名构造函数} 错误示例.1——太多的同名构造函数
//错误示例.1——太多的同名构造函数
/* 创建一个描述账单的类,该类有多个字段,多个构建函数对应不同的账单 */
class Bill{
/* 全构造函数(包含所有参数) */
public Bill(int id,String name,Type type,int money,Date date,String owner,...){
...}
/* 构造一个普通账单,id以及date等都自动分配 */
public Bill(String name,Type type,int money){
...}
/* 构造一个特殊支出账单,表示该支出是持久的 */
public Bill(String name,int money,Type specialType){
}//这里故意将位置调整一下,模拟错误
...
//more public Bill(){}
}
class Client{
public void useBill(){
/* 该怎么构建Bill呢? 这要求使用者熟悉Bill构建详细内容 */
Bill bill=new Bill(???);
}
}
该问题可以归类为代码难以理解中,使用简化重构——其核心思想是使得代码更好读懂,结构更合理,方法之一是使用创建方法封装同名构造函数——详解示例.1——使用创建方法封装
错 误 示 例 . 2 — — 创 建 职 责 过 多 且 分 散 \color{red}{错误示例.2——创建职责过多且分散} 错误示例.2——创建职责过多且分散
//错误示例.2——创建职责过多且分散
/* 稍微改进上一个错误示例,简单封装实现了自我工厂(自己负责自己创建) */
class Bill{
/* 全构造函数(包含所有参数) */
private Bill(int id,String name,Type type,int money,Date date,String owner,...){
...}
/* 构造一个普通账单,id以及date等都自动分配 */
public static Bill defaultBill(String name,Type type,int money){
...}
/* 构造一个特殊支出账单,表示该支出是持久的 */
public static Bill staticOutBill(String name,Type specialType,int money){
}//这里故意将位置调整一下,模拟错误
...
//more public static Bill XXXBill(){}——这里你可以创建一些Bill的子类实现更多的功能
}
/* 尽管现在我们将创建函数名称和创建出的Bill描述结合起来了,但是过多的创建函数占用了Bill太多内容,而且static函数?不是很有必要呆在Bill中,这不应该是它的责任 */
过多的创建职责——使得你的类就像钞票能够负责自己印刷不同面额一样奇怪 ,很容易想到的是使用一个 简单工厂负责创建功能——见示例.2——使用工厂集中创建职责
错 误 示 例 . 3 — — 相 同 逻 辑 的 重 复 \color{red}{错误示例.3——相同逻辑的重复} 错误示例.3——相同逻辑的重复
//错误示例.3——相同逻辑的重复
class RootNodeTest{
public void showRootTest(){
/* 处理代码中创建部分不同之外,其余部分大致相同 */
Node node=new Root();
node.addChild("next1");
node.addChild("next2");
//do other sameWork
node.show();//使用root的show测试结果
}
}
class NomalNodeTest{
public void showNodeTest(){
Node node=new NomalNode();
node.addChild("next1");
node.addChild("next12");
//do other sameWork
node.show();//使用nomalNode的show测试
}
}
过多的重复工作使得代码复用性不高,而且增大了理解每个部分的成本,可以使用Factory Method方法求同存异,提取公共部分到父类,使得子类去实现具体的创建方法
二、对应的重构方法
示 例 . 1 — — 使 用 创 建 方 法 “ 重 命 名 ” 同 名 构 造 方 法 \color{green}{示例.1——使用创建方法“重命名”同名构造方法} 示例.1——使用创建方法“重命名”同名构造方法
//示例.1——使用创建方法“重命名”同名构造方法
/* 对于一个简单的类,多个同名的构造方法使得其使用不便,使用创建方法为构造函数加以描述 */
class Bill{
private final int id;
...
public static Bill creatBillByMoney(int i){
/* 使用创建方法包装了很多默认构造选项,使得使用更方便,而且名字更具体 */
return new Bill(getId(),"默认",TYPE.DEFAULT,getDate(),i);
}
public Bill(int id,String name,Type type,Date date,int money){
...}
...
}
class Client{
public void use(){
//Bill b=new Bill(getId(),"默认",TYPE.DEFAULT,getDate(),i);
Bill b=creatBillByMoney(100);
}
}
示 例 . 2 — — 使 用 简 单 工 厂 统 一 对 象 创 建 职 责 \color{green}{示例.2——使用简单工厂统一对象创建职责} 示例.2——使用简单工厂统一对象创建职责
//示例.2——使用简单工厂统一对象创建职责
class BillFactory extend Factory{
public Bill creatBillByMoney(...){
...};
public Bill creatBillWithTag(...){
...};
//...
/* 封装一些细节处理方法帮助创建对象,这部分功能不应该在原类中提炼 */
private boolean dealDetails(...){
...};
//...
}
示 例 . 3 — — 使 用 工 厂 方 法 提 取 共 有 代 码 , 保 留 多 态 创 建 \color{green}{示例.3——使用工厂方法提取共有代码,保留多态创建} 示例.3——使用工厂方法提取共有代码,保留多态创建
//示例.3——使用工厂方法提取共有代码,保留多态创建
abstract class NodeTest{
public void showNodeTest(){
Node node=creatNode();
node.addChild("next1");
node.addChild("next2");
//do other sameWork
node.show();//使用nomalNode的show测试
}
abstract Node creatNode();
}
/* 省去了重复写一大堆逻辑代码,只需要关注其不同的部分即可,下面两个类都相当于一个简单工厂类 */
class RootNodeTest extend NodeTest{
@Override
public Node creatNode(){
return new RootNode();
}
}
class NomalNodeTest extend NodeTest{
@Override
public Node creatNode(){
return new NomalNode();
}
}
三、重构图示
重构步骤:
- 找到多个同名构造函数职责不清晰、或者构造过程长的代码
- 创建方法封装构造函数,提炼相同逻辑
- 根据需要(创建方法过多,原类不需要承担构造职责),提炼出简单工厂类
- 如果存在多个工厂,且部分处理逻辑相同,提炼出工厂方法类
四、问题回顾
1. 主 要 应 用 场 景 ? \color{blue}{1.主要应用场景?} 1.主要应用场景?
答:重点关注创建对象时,如果同名构造函数过多不利于使用-使用创建函数包装,如果创建任务职责不当-使用工厂类集中创建职责,如果创建对象的处理方法重复-提取工厂方法类
2. 一 定 需 要 重 构 吗 ? \color{blue}{2.一定需要重构吗?} 2.一定需要重构吗?
答:重构的目的是简化理解和代码易扩展,如果完全没有扩展需求或者代码体量很小,比如不必为了一个创建职责而单独包装一个工厂类。但是如果重复代码,过多的对象创建导致了你的代码臃肿不堪,难以理解——不要犹豫,立刻开始重构,不要使得问题越来越多
3. 重 构 以 趋 向 或 实 现 F a c t o r y 模 式 有 什 么 帮 助 ? \color{blue}{3.重构以趋向或实现Factory模式有什么帮助?} 3.重构以趋向或实现Factory模式有什么帮助?
答:使得对象构建易于理解,构建与使用解耦(修改对象的创建过程不会导致使用对象的代码大量修改);使得职责划分更合理,钞票不会自己负责印刷自己;解决部分由于构建不同对象的代码重复问题
欢迎关注我、共勉⭐️
⭐️⭐️代码之狐⭐️⭐️
主要内容:
- 时不时更新算法题解,算法与数据结构
- 时不时分享心灵鸡汤,详见杂谈栏
- 目前主要在学Java高级内容(虚拟机、框架什么的),以及很重要的软件工程、重构和设计模式等,会将书中的知识点提炼总结分享