实用程序类一般都是包含的静态方法。它通常是用来包装一些的“有用”的算法。

StringUtils, IOUtils, FileUtils from Apache Commons; Iterables and Iterators from Guava, and Files from JDK7 are perfect examples of utility classes.

实用程序类的必要性

If you have two classes A and B, and have a method f() that both must use, then the most naive approach is to repeat the function as a method in both classes. However, this violates the Don’t repeat yourself (DRY) approach to programming.

The most natural solution is inheritance, but it’s not always beneficial for A and B to be subclasses of some parent class. In my case, A was already a subclass of another class, while Bwas not. There was no way to make A and B subclasses of a parent class without breaking that other relationship.

The alternative is to define the utility class: a public static class that sits in the global namespace, awaiting anyone to “borrow” them.

They are not bad in themselves, but they do imply relationships between your data that are not explicitly defined. Also, if your static class has any static variables, then A and B never really know what they’re getting into when they use it.

Disadvantage

However, in an object-oriented world, utility classes are considered a very bad practice.

The use of utility classes to be an antipattern. More specifically, it violates common design principles:

Single Responsibility Principle

A class should have one and only one reason to change

You can design utility classes where all of the methods related to a single set of responsibilities. That is entirely possible. Therefore, I would note that this principle does not conflict with the notion of utility classes at all.

That said, I’ve often seen helper classes that violate this principle. They become “catch all” classes (or God Classes) that contain any method that the developer can’t find another place for. (e.g. a class containing a helper method for URL encoding, a method for looking up a password, and a method for writing an update to the config file… This class would violate the Single Responsibility Principle).

Liskov Substitution Principle

Derived classes must be substitutable for their base classes

This is kind of a no-op in that a utility class cannot have a derived class. OK. Does that mean that utility classes violate LSP? I’d say not. A helper class looses the advantages of OO completely, an in that sense, LSP doesn’t matter… but it doesn’t violate it.

Interface Segregation Principle

Class interfaces should be fine-grained and client specific

another no-op. Since utility classes do not derive from an interface, it is difficult to apply this principle with any degree of seperation from the Single Responsibility Principle.

The Open Closed Principle

classes should be open for extension and closed for modification

You cannot extend a utility class. Since all methods are static, you cannot derive anything that extends from it. In addition, the code that uses it doesn’t create an object, so there is no way to create a child object that modifies any of the algorithms in a utility class. They are all “unchangable”.

As such, a helper class simply fails to provide one of the key aspects of object oriented design: the ability for the original developer to create a general answer, and for another developer to extend it, change it, make it more applicable. If you assume that you do not know everything, and that you may not be creating the “perfect” class for every person, then utility classes will be an anathema to you.

The Dependency Inversion Principle

Depend on abstractions, not concrete implementations

This is a simple and powerful principle that produces more testable code and better systems. If you minimize the coupling between a class and the classes that it depends upon, you produce code that can be used more flexibly, and reused more easily.

However, a utility class cannot participate in the Dependency Inversion Principle. It cannot derive from an interface, nor implement a base class. No one creates an object that can be extended with a helper class. This is the “partner” of the Liskov Substitution Principle, but while utility classes do not violate the LSP, they do violate the DIP.

In summary, utility classes are not proper objects; therefore, they don’t fit into object-oriented world. They were inherited from procedural programming, mostly because most were used to a functional decomposition paradigm back then.

Object-Oriented Alternative

Example1

Let’s take NumberUtils for example:

Utility Class

// This is a terrible design, don't reuse

public class NumberUtils {

public static int max(int a, int b) {

return a > b ? a : b;

}

}

In an object-oriented paradigm, we should instantiate and compose objects, thus letting them manage data when and how they desire. Instead of calling supplementary static functions, we should create objects that are capable of exposing the behaviour we are seeking:

OO Class

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;

}

}

This procedural call:

int max = NumberUtils.max(10, 5);

Will become object-oriented:

int max = new Max(10, 5).intValue();

Example2

Say, for instance, you want to read a text file, split it into lines, trim every line and then save the results in another file. This is can be done with FileUtils from Apache Commons:

Utility Class

void transform(File in, File out) {

Collection src = FileUtils.readLines(in, "UTF-8");

Collection dest = new ArrayList<>(src.size());

for (String line : src) {

dest.add(line.trim());

}

FileUtils.writeLines(out, dest, "UTF-8");

}

The above code may look clean; however, this is procedural programming, not object-oriented. We are manipulating data (bytes and bits) and explicitly instructing the computer from where to retrieve them and then where to put them on every single line of code. We’re defining a procedure of execution.

The OO alternative is:

OO classes

void transform(File in, File out) {

Collection src = new Trimmed(

new FileLines(new UnicodeFile(in))

);

Collection dest = new FileLines(

new UnicodeFile(out)

);

dest.addAll(src);

}

FileLines implements Collection and encapsulates all file reading and writing operations. An instance of FileLines behaves exactly as a collection of strings and hides all I/O operations. When we iterate it — a file is being read. When we addAll() to it — a file is being written.

Trimmed also implements Collection and encapsulates a collection of strings (Decorator pattern). Every time the next line is retrieved, it gets trimmed.

All classes taking participation in the snippet are rather small: Trimmed, FileLines, and UnicodeFile. Each of them is responsible for its own single feature, thus following perfectly the single responsibility principle.

On our side, as users of the library, this may be not so important, but for their developers it is an imperative. It is much easier to develop, maintain and unit-test class FileLines rather than using a readLines() method in a 80+ methods and 3000 lines utility class FileUtils. Seriously, look at its source code.

Lazy Execution

An object-oriented approach enables lazy execution. The in file is not read until its data is required. If we fail to open out due to some I/O error, the first file won’t even be touched. The whole show starts only after we call addAll().

All lines in the second snippet, except the last one, instantiate and compose smaller objects into bigger ones. This object composition is rather cheap for the CPU since it doesn’t cause any data transformations.

Besides that, it is obvious that the second script runs in O(1) space, while the first one executes in O(n). This is the consequence of our procedural approach to data in the first script.

In an object-oriented world, there is no data; there are only objects and their behavior!

References

java实用程序_java面向对象设计之实用程序类相关推荐

  1. java面向对象设计_Java面向对象设计

    全屏 本教程将通过示例了解Java中面向对象(OOP)的概念.讨论面向对象编程的特性,以及编写面向对象程序包括创建类,并从这些类创建对象,以及创建应用程序,这些应用程序是使用这些对象的独立可执行程序. ...

  2. java面向对象设计_Java面向对象设计 构造函数设计

    在Javascript面向对象设计一--工厂模式 中介绍了使用CreateEmployee()函数创建员工类.ECMAScript中的构造函数可以用来创建特定类型的对象,如Object和Array这样 ...

  3. 电子记事本java代码_JAVA课程设计报告电子钟日历记事本

    JAVA课程设计报告电子钟日历记事本 课课程程设设计计 报报告告 ( 2012-- 2013 年度第年度第 2 学期学期) 日历记事本日历记事本 专业专业计算机科学与技术计算机科学与技术 学生姓名学生 ...

  4. 记忆测试系统java代码_JAVA课程设计——记忆测试系统(附源程序).doc

    <JAVA程序设计>课程设计报告 题 目: 记忆游戏 姓 名: 学 号: 班 级: 指导教师: 完成时间 成 绩: 信息工程学院 2015年6月 目 录 TOC \o "1-5& ...

  5. JAVA实验二:设计一个教师类Teacher(属于cn.net.sdkd包)实现接口进行排序等

    设计一个教师类Teacher(属于cn.net.sdkd包) (1)属性有编号(int no).姓名(String name).年龄(int age).所属学院(String seminary),为这 ...

  6. Java基础 接口实现 设计一个形状类(接口)Shape,方法:求周长和求面积

    题目: 设计一个形状类(接口)Shape,方法:求周长和求面积形状类(接口)的子类(实现类);:Rect(矩形),Circle(圆形)Rect类的子类:Square(正方形)不同的子类会有不同的计算周 ...

  7. 消灭泡泡糖游戏java编程_JAVA面向对象编程课程设计——泡泡堂

    ###一.成员介绍 姓名|任务分配|个人博客链接 --|:--:|--: 张一弛(组长)|GUI设计.游戏界面相关类实现.阿里巴巴扫描纠错.团队博客编写|在做了,在做了,下次一定 王江鸿|Box类.B ...

  8. 网上选课系统java大作业_Java面向对象设计大作业——公选课选课系统

    功能调查与系统功能框架图 功能 本系统模拟公选课选课系统,由学生或管理员使用该系统,管理员可以自定义增加或删除课程,学生可以进行选课和查看已选课程. 系统功能框架图 类的设计(UML类图) 类说明 A ...

  9. java如何创建一个dao类_java – 如何设计一个DAO类?

    应该是什么是设计DAO类的最佳方式? 方法#1:将DAO类设计为对象. class Customer { //customer class } class CustomerDAO { public v ...

最新文章

  1. tomcat历史版本新特性_tomcat升级后报错RFC 7230 and RFC 3986
  2. 个人开发者做一款Android App需要知道的事情
  3. CTF(pwn)-格式化字符串漏洞讲解(二) --攻防世界CGfsb
  4. C# 调用WSDL接口及方法
  5. UICollectionView的sectionHeader悬浮效果
  6. 小明爱跑步-扩展-多个对象属性之间互不干扰
  7. ANTLR和StringTemplate
  8. mysql 创建用户并授权_教你MySQL-8.0.x数据库授权
  9. 中兴通讯与北京交通大学、中国移动北京公司签署 “5G战略合作框架协议”
  10. html中文本框冒号对齐,html5 冒号分隔符对齐的实现,
  11. qtxlsx编译报错_Qt5安装Qxlsx模块,以及Qxlsx模块的简单使用,附资源例程下载
  12. python结构模式图_NetworkX:Python图与网络模型基础
  13. Linux学习笔记(1)linux的开关机及重启
  14. macOS Big Sur M1机型,如何正确完成软件的系统扩展设置?
  15. 78. Cookie
  16. Java实现顺序表的创建及基本操作(增删改查)
  17. 统信自带浏览器js不兼容问题
  18. OCCT示例学习笔记3--Modeling项目
  19. python中无法安装xpath库,Python爬虫 | xpath的安装
  20. Android中仿微信选择图片并展示在RecyclerView中

热门文章

  1. 【秒懂音视频开发】06_重识声音
  2. vue3滑块验证使用vue3-slide-verify,宽度自适应
  3. 区别动态链接库和静态链接库
  4. 人脸识别 | 论文参考
  5. Android 开发过程中遇到的棘手的问题笔记(SP引起的ANR,4G网络请求慢,app启动白屏)(持续更新)
  6. Python使用openCV获得图片的h,w,c
  7. 【chip-tool】chip-tool工具常用指令介绍
  8. 用 ipdb 调试 Python 程序
  9. 数学建模常见的一些方法【03插值算法】
  10. 软件版本号的意思是什么?从PC到安卓,带你了解版本号命名逻辑