什么
重构是一种用于使代码更具可读性和
对人类而言是可以理解的,从而可以维护。 它不会影响
功能。 实际上,它不应该影响功能。
为什么
当您让其他人阅读您的代码时,重构是必要的。 因此,如果您在休息后重新访问代码,可能还会发现
很难理解。 重构也使添加新的内容更容易
功能,并供其他人在其代码中使用。 就是这样
重构代码很重要。
怎么样
让我们看下面的例子。 假设我需要编写如下程序:
名称:“ extract-properties ”。
描述: 从文件中提取并打印属性值
输入:
- 文件 (类型为xml |属性| json ..)和
- 要提取的值的 属性名称
- 动态地图 (用相应的地图值替换在属性值中找到的动态地图键(如果有)。)
现在我们可以将问题分解为较小的问题,例如:
- 从给定文件中提取属性( Extract方法应该支持文件类型,例如xml,property,json。)
- 用动态值替换占位符
有三种解决方法。
- 程序
- 面向对象
- 功能性
让我们评估所有三个。
程序
解决问题的程序方式,
- main方法读取文件内容并调用提取方法。
- extract方法使用if条件调用类型,根据类型解析内容
特定的解析方法并提取属性并返回这些属性
属性。 - main方法现在调用replace方法,以其动态值替换占位符。
现在,假设您需要添加另一个处理程序,例如“ YAML”文件类型。 您将需要:
- 在“提取”方法中添加另一个if else块。
上述方法的优点:
- 当扩展/修改受到限制时,易于编写。
上述方法的缺点是什么?
- 这会导致提取方法出现瓶颈。 在一个开发人员团队中,所有开发人员对单个实体的不断更新将导致合并冲突。
- 随着添加扩展,提取方法的大小将增加。 这将使其可读性降低。
- 在I / O操作中穿插业务逻辑将导致单元测试用例,而这需要实际资源而不是模拟资源。
- 由于资源I / O操作代码与业务逻辑紧密耦合,因此无法在代码的其他部分中重复使用。
面向对象
让我们有一个基类ResourceHandler
。 这定义了方法extract()和replace ()
。 extract()
需要允许自定义实现以支持将来的增强。 尽管replace()
必须是通用逻辑,但与自定义提取实现无关。
我们可以这样建模:

如您所见,ResourceHandler具有方法:
- 处理()。 它包含业务逻辑,就我们的目的而言,它可以很简单:
public Map handle () { extract(); replace(); }
- 受保护的抽象void extract()-包含从文件中提取属性的逻辑
- private void replace()-包含用于替换动态映射中的属性的逻辑
现在,抽象方法“提取”需要被其子类覆盖:
- XmlResource
- 杰森资源
- 属性资源
以下是主要课程。

这种方法的优点:
- 这种结构允许添加新的扩展名而无需修改基本的“ handler”方法,该方法被调用以提取和替换属性。 因此,子类不能破坏业务逻辑。
- 提取方法不再像以前那样存在瓶颈
程序代码。 现在每种自定义提取方法都有自己的方法
在自己的类中实现。 这将减少可能的
从事不同自定义提取实现的多个开发人员之间的合并冲突。 - 关闭对象“ ResourceHandler”以进行修改,同时打开其扩展。 这是SOLID的支柱之一
原理。 例如,如果ResourceHandler作为jar共享,则可以
仍然可以扩展以支持其他资源类型,但是
基础基本逻辑仍然受到限制。
缺点:
- 用于提取给定类型的属性的IO逻辑仍与
业务逻辑,由于基类的结构。 作为唯一
公共方法是handle,它调用extract方法并替换
方法。 因此,我们不能孤立地使用提取。
功能性
让我们使用如下接口来实现功能代码:

ResourceHandler接口定义单个方法“提取”,该方法提取文件和要提取的属性列表,然后从输入文件返回相应的提取属性。
我们将在业务逻辑中利用ResourceHandler,并注入
封装在类ExtractReplace中的特定实现
业务逻辑如下:
class ExtractReplace {
private Map extracted_properties;
public ExtractReplace (File file, List properties,Map dynamic, ResourceHandler handler) {
Map props = handler.extract(file, properties);
extracted_properties = replace(props, dynamic);
}
private Map replace (Map properties, Map dynamic) {...}
@Override
public String toString () {
return extracted_properties.toString();
}
}
现在,主类将如下所示:

主类通过传递一个类型来创建ExtractReplace类型的对象
通过Lambda构造实现ResourceHandler接口
看过。 这演示了定义即席匿名的能力
不需要放在自己的类型中的实现,
从而降低结构复杂度。
我们还可以遵循一种更加结构化的方法,其中不同风格的ResourceHandlers存放在它们自己的类型中。

这种结构允许独立使用XmlHandler / JsonHandler…对象来提取属性。 与上面所示的面向对象的方法相比,这是一个优势。
主类可以创建特定类型的ResourceHandler的对象,并将它们传递给ExtractReplace类,如下所示:

您可以通过具有构建器类(使用构建器模式)来进一步解耦if-else代码,以实例化ResourceHandler。

具有'builder'模式的增强的ExtractReplace类将如下所示:
class ExtractReplace {
private Map extracted_properties;
static class Builder {
private File file;
private List properties;
private Map dynamic;
private ResourceHandler handler;
static Builder getInstance (File file, List properties, Map dynamic) {
this .file = file;
this .properties = properties;
this .dynamic = dynamic;
if (file.getName().endsWith( ".json" ))
this .handler = new JsonHandler();
else if (file.getName().endsWith( ".xml" ))
this .handler = new XmlHandler();
}
public ExtractReplace build () {
new ExtractReplace( this );
}
}
private ExtractReplace (Builder builder) {
Map props = builder.handler.extract(builder.file, builder.properties);
}
private Map replace (Map properties, Map dynamic) { ... }
@Override
public String toString () {
return extracted_properties.toString();
}
}
更多关于构建器模式的信息 。
优点:
- 除了面向对象方法提供的优势之外,这还将提取逻辑与业务逻辑分离。 因此,您可以重用此提取方法。
- 您可以通过创建可以重用的ResourceHandler的不同实现类来遵循结构化方法。
- 您还可以使用Lambda函数遵循即席匿名方法。 如果您不打算在其他地方重用提取逻辑,而又想降低结构复杂性,则这很有用。
最后说明
上面的功能代码是Strategy模式的实现,类似于Collections.sort,您可以在其中传递自定义的Comparator
实施。 如您所见,代码是可扩展的,更容易
消费,理解和扩展。
From: https://hackernoon.com/refactoring-using-functional-programming-g682o30g6