java oop
实用程序类(也称为帮助程序类)是仅具有静态方法且不封装任何状态的“结构”。 StringUtils
, IOUtils
, FileUtils
从Apache的共享; Guava的Iterables
和Iterators
以及JDK7的Files
是实用程序类的完美示例。
这种设计思想在Java世界(以及C#,Ruby等)中非常流行,因为实用程序类提供了在各处使用的通用功能。
在这里,我们要遵循DRY原则并避免重复。 因此,我们将通用代码块放入实用程序类中,并在必要时重新使用它们:
// This is a terrible design, don't reuse
public class NumberUtils {
public static int max(int a, int b) {
return a > b ? a : b;
}
}
确实,这是一种非常方便的技术!
实用程序类是邪恶的
但是,在面向对象的世界中,实用程序类被认为是一种非常糟糕的做法(有些甚至可能说“可怕”)。
关于这个主题有很多讨论。 仅举几例: Helper Classs Evil? 尼克·马利克(Nick Malik)撰写的《 为什么西蒙·哈特(Simon Hart)的助手,单身人士和实用程序类大多不好,沃德元帅避免使用实用程序类,杀死实用程序类! 由Dhaval Dalal撰写,帮助类是Rob Bagby的代码气味。
此外,在StackExchange上还有一些关于实用程序类的问题: 如果“实用程序”类是邪恶的,那么我应该将通用代码放在哪里? ,实用程序类是邪恶的。
他们所有论点的简要总结是,实用程序类不是正确的对象。 因此,它们不适合面向对象的世界。 它们是从过程编程中继承的,主要是因为大多数方法都用于那时的功能分解范例。
假设您同意这些参数并且想停止使用实用程序类,我将通过示例展示如何用适当的对象替换这些生物。
程序示例
举例来说,假设您要读取一个文本文件,将其拆分为几行,修剪每一行,然后将结果保存到另一个文件中。 这可以通过Apache Commons的FileUtils
完成:
void transform(File in, File out) {
Collection<String> src = FileUtils.readLines(in, "UTF-8");
Collection<String> dest = new ArrayList<>(src.size());
for (String line : src) {
dest.add(line.trim());
}
FileUtils.writeLines(out, dest, "UTF-8");
}
上面的代码看起来很干净; 但是,这是过程编程,而不是面向对象的。 我们正在处理数据(字节和位),并明确指示计算机从何处检索数据,然后再在何处将其放置在每一行代码中。 我们正在定义执行程序。
面向对象的替代
在面向对象的范例,我们应该实例和撰写的对象,从而让他们管理数据时,他们如何渴望。 与其调用补充静态函数,不如创建能够公开我们正在寻求的行为的对象:
public class Max implements Number {
private final int a;
private final int b;
public Max(int x, int y) {
this.a = x;
this.b = y;
}
@Override
public int intValue() {
return this.a > this.b ? this.a : this.b;
}
}
此过程调用:
int max = NumberUtils.max(10, 5);
将成为面向对象的:
int max = new Max(10, 5).intValue();
土豆土豆并不是的; 只是继续阅读...
对象而不是数据结构
这就是我将如何设计与上述相同的文件转换功能,但是以面向对象的方式:
void transform(File in, File out) {
Collection<String> src = new Trimmed(
new FileLines(new UnicodeFile(in))
);
Collection<String> dest = new FileLines(
new UnicodeFile(out)
);
dest.addAll(src);
}
FileLines
实现Collection<String>
并封装所有文件读写操作。 FileLines
实例的行为与字符串的集合完全相同,并且隐藏了所有I / O操作。 当我们迭代它时—正在读取一个文件。 当我们addAll()
时—正在写入文件。
Trimmed
还实现了Collection<String>
并封装了字符串的集合( Decorator模式)。 每次检索下一行时,都会对其进行修剪。
所有服用参与片断类是相当小: Trimmed
, FileLines
和UnicodeFile
。 他们每个人都对自己的单一功能负责,因此完全遵循单一责任原则。
在我们这方面,作为库的用户,这可能并不那么重要,但是对于他们的开发人员而言,这势在必行。 与在80多个方法和3000行实用程序类FileUtils
使用readLines()
方法相比,开发,维护和测试FileLines
类要容易得多。 认真地看一下其源代码。
面向对象的方法使延迟执行成为可能。 in
需要输入数据之前,不会读取in
文件。 如果我们不能开out
由于一些I / O错误,第一个文件甚至不能触及。 整个节目只有在我们调用addAll()
之后才开始。
除了最后一个片段外,第二个片段中的所有行都实例化并将较小的对象组合为较大的对象。 该对象组合对于CPU而言相当便宜,因为它不会引起任何数据转换。
除此之外,很明显第二个脚本在O(1)空间中运行,而第一个脚本在O(n)中运行。 这是我们对第一个脚本中的数据采用过程方法的结果。
在面向对象的世界中,没有数据。 只有对象及其行为!
相关文章
您可能还会发现以下有趣的帖子:
翻译自: https://www.javacodegeeks.com/2014/09/oop-alternative-to-utility-classes.html
java oop