工具类编写规范

本文内容摘抄自晓风轻,链接:https://zhuanlan.zhihu.com/p/29199049

1、定义自己的工具类,隐藏实现,封装/解耦的思想。

尽量不要在业务代码里面直接调用第三方的工具类。这也是解耦的一种体现。如果我们不定义自己的工具类而是直接使用第三方的工具类有2个不好的地方:不同的人会使用不同的第三方工具库,会比较乱。将来万一要修改工具类的实现逻辑会很痛苦。

以最简单的字符串判空为例,很多工具库都有 StringUtils工具类,如果我们使用commons的工具类,一开始我们直接使用 StringUtils.isEmpty ,字符串为空或者空串的时候会返回为true,后面业务改动,需要改成如果全部是空格的时候也会返回true,怎么办?我们可以改成使用 StringUtils.isBlank 。看上去很简单,对吧? 如果你有几十个文件都调用了,那我们要改几十个文件,是不是有点恶心?再后面发现,不只是英文空格,如果是全角的空格,也要返回为true,怎么办?StringUtils上的方法已经不能满足我们的需求了,真不好改了。。。

所以我的建议是,一开始就自己定义一个自己项目的StringUtil,里面如果不想自己写实现,可以直接调用commons的方法,如下:

public static boolean isEmpty(String str) {

return org.apache.commons.lang3.StringUtils.isEmpty(str);

}

后面全部空格也返回true的时候,我们只需要把isEmpty改成isBlank;再后面全部全角空格的时候也返回true的话,我们增加自己的逻辑即可。我们只需要改动和测试一个地方。

2、复制对象的属性方法,隐藏实现,封装/解耦的思想。

一开始,如果我们自己不定义工具类方法,那么我们可以使用 org.springframework.beans.BeanUtils.copyProperties(source, dest) 这个工具类来实现,就一行代码,和调用自己的工具类没有什么区别。看上去很OK,对吧?

随着业务发展,我们发现这个方式的性能或者某些特性不符合我们要求,我们需要修改改成 commons-beanutils包里面的方法,org.apache.commons.beanutils.BeanUtils.copyProperties(dest, source),这个时候问题来了,第一个问题,它的方法的参数顺序和之前spring的工具类是相反的,改起来非常容易出错!第二个问题,这个方法有异常抛出,必须声明,这个改起来可要命了!结果你发现,一个看上去很小的改动,改了几十个文件,每个改动还得测试一次,风险不是那么得小。有一点小奔溃了,是不是?

等你改完之后测试完了,突然有一天需要改成,复制参数的时候,有些特殊字段需要保留(如对象id)或者需要过滤掉(如密码)不复制,怎么办?这个时候我估计你要崩溃了吧?不要觉得我是凭空想象,编程活久见,你总会遇到的一天!

所以,我们需要定义自己的工具类函数,一开始我定义成这样子。

public void copyAttribute(Object source, Object dest) {

org.springframework.beans.BeanUtils.copyProperties(source, dest);

}

后面需要修改为commons-beanutis的时候,我们改成这样即可,把参数顺序掉过来,然后处理了一下异常,我使用的是Lombok的SneakyThrows来处理异常,你也可以捕获掉抛出运行时异常,个人喜好。

@SneakyThrows

public void copyAttribute(Object source, Object dest) {

org.apache.commons.beanutils.BeanUtils.copyProperties(dest, source);

}

再后面,复制属性的时候需要保留某些字段或者过滤掉某些字段,我们自己参考其他库实现一次即可,只改动价格和测试一个文件一个方法,风险非常可控。

3、使用父类、接口。抽象的思想。

举例,假设我们写了一个判断arraylist是否为空的函数,一开始是这样的。

public static boolean isEmpty(ArrayList<?> list) {

return list == null || list.size() == 0;

}

这个时候,我们需要思考一下参数的类型能不能使用父类。我们看到我们只用了size方法,我们可以知道size方法再list接口上有,于是我们修改成这样。

public static boolean isEmpty(List<?> list) {

return list == null || list.size() == 0;

}

后面发现,size方法再list的父类/接口Collection上也有,那么我们可以修改为最终这样。

public static boolean isEmpty(Collection<?> list) {

return list == null || list.size() == 0;

}

到了这部,Collection没有父类/接口有size方法了,修改就结束了。最后我们需要把参数名字改一下,不要再使用list。改完后,所有实现了Collection都对象都可以用,最终版本如下:

public static boolean isEmpty(Collection<?> collection) {

return collection == null || collection.size() == 0;

}

是不是看上去通用多了 ,看上去也专业多了?上面的string相关的工具类方法,使用相同的思路,我们最终修改一下,把参数类类型由String修改为CharSequence,参数名str修改为cs。如下:

public static boolean isEmpty(CharSequence cs) {

return org.apache.commons.lang3.StringUtils.isEmpty(cs);

}

思路和方法很简单,但效果很好,写出来的工具类也显得很专业!总结一下,思路是抽象的思想,主要是修改参数类型,方法就是往上找父类/接口,一直找到顶为止,记得修改参数名。

4、使用重载编写衍生函数组。方法参数尽量抽象,而返回值尽量具体。

开发过的兄弟都知道,有一些工具库,有一堆的重载函数,调用起来非常方便,经常能直接调用,不需要做参数转换。这些是怎么样编写出来的呢?我们举例说明。现在需要编写一个方法,输入是一个utf-8格式的文件的文件名,把里面内容输出到一个list<String>。我们刚刚开始编写的时候,是这个样子的:

public static List<String> readFile2List(String filename) throws IOException {

List<String> list = new ArrayList<String>();

File file = new File(filename);

FileInputStream fileInputStream = new FileInputStream(file);

BufferedReader br = new BufferedReader(new InputStreamReader(fileInputStream,

"UTF-8"));

// XXX操作

return list;

}

我们先实现,实现完之后我们做第一个修改,很明显,utf-8格式是很可能要改的,所以我们先把它做为参数提取出去,方法一拆为二,就变成这样。

public static List<String> readFile2List(String filename) throws IOException {

return readFile2List(filename, "UTF-8");

}

public static List<String> readFile2List(String filename, String charset)

throws IOException {

List<String> list = new ArrayList<String>();

File file = new File(filename);

FileInputStream fileInputStream = new FileInputStream(file);

BufferedReader br = new BufferedReader(new InputStreamReader(fileInputStream,

charset));

// XXX操作

return list;

}

多了一个方法,直接调用之前的方法主体,主要的代码还是只有一份,之前的调用地方不需要做任何修改!可以放心修改。

然后我们在看里面的实现,下面这2行代码里面,String类型的filename会变化为File类型,然后在变化为FileInputStream 类型之后才使用。

File file = new File(filename);

FileInputStream fileInputStream = new FileInputStream(file);

这里我们就应该想到,用户可能直接传如File类型,也可能直接传入FileInputStream类型,我们应该都需要支持,而不需要用户自己做类型的处理!在结合上一点的使用父类,把FileInputStream改成父类InputStream,我们最终的方法组如下:

package plm.common.utils;

import java.io.BufferedReader;

import java.io.File;

import java.io.FileInputStream;

import java.io.IOException;

import java.io.InputStream;

import java.io.InputStreamReader;

import java.util.ArrayList;

import java.util.List;

import org.apache.commons.io.IOUtils;

/**

* 工具类编写范例,使用重载编写不同参数类型的函数组

*

*/

public class FileUtil {

private static final String DEFAULT_CHARSET = "UTF-8";

public static List<String> readFile2List(String filename) throws IOException {

return readFile2List(filename, DEFAULT_CHARSET);

}

public static List<String> readFile2List(String filename, String charset)

throws IOException {

FileInputStream fileInputStream = new FileInputStream(filename);

return readFile2List(fileInputStream, charset);

}

public static List<String> readFile2List(File file) throws IOException {

return readFile2List(file, DEFAULT_CHARSET);

}

public static List<String> readFile2List(File file, String charset)

throws IOException {

FileInputStream fileInputStream = new FileInputStream(file);

return readFile2List(fileInputStream, charset);

}

public static List<String> readFile2List(InputStream fileInputStream)

throws IOException {

return readFile2List(fileInputStream, DEFAULT_CHARSET);

}

public static List<String> readFile2List(InputStream inputStream, String charset)

throws IOException {

List<String> list = new ArrayList<String>();

BufferedReader br = null;

try {

br = new BufferedReader(new InputStreamReader(inputStream, charset));

String s = null;

while ((s = br.readLine()) != null) {

list.add(s);

}

} finally {

IOUtils.closeQuietly(br);

}

return list;

}

}

6个方法,实际上代码主体只有一份,但提供各种类型的入参,调用起来很方便。开发组长编写的时候,多费一点点时间,就能写来看上去很专业调用起来很方便的代码。如果开发组长不写好,开发人员发现现有的方法只能传String,她要传的是InputStream,她又不敢改原来的代码,就会copy一份然后修改一下,就多了一份重复代码。代码就是这样烂下去了。。。关键点,多想一步,根据参数变化编写各种类型的入参函数,需要保证函数主要代码只有一份。

5、使用静态引入,工具类的一个问题就是容易泛滥,主要原因是开发人员找不到自己要用的方法,就自己写一个,开发人员很难记住类名,所以要让开发人员容易找到,我们可以使用静态引入,在Eclipse里面这样导入:

集成到jdk 然后又都是静态的,集成到jdk就可以不管是否导入包 都可以使用,静态的就可以确保 不需要去new 对象。

面向对象的思想有抽象封装,其实有心的话,处处都能体现出这些思想。编写工具类的时候需要注意参数的优化,而且大型项目里面不要在业务代码里面直接调用第三方的工具类,然后就是多想一步多走一步,考虑各种类型的入参,这样你也能编写出专业灵活的工具类。

原文地址:https://www.cnblogs.com/wanghaichao/p/9167403.html

时间: 2024-11-02 02:15:19

工具类编写规范的相关文章

Android Java类编写规范

本文仅是我个人在实际开发中习惯的编写方式,当然这种方式也是来自很多官方的推荐,所以在一定程度上是可以被模仿套用的.本文将不定期更新~ 使用特殊转义序列而不是它的八进制 我们应该用特殊的转移序列,比如:(\b, \t, \n, \f, \r, \”, \’及\),而不是它们对应的八进制,比如:\012 大胆使用非ASCII字符 如果我们在实际中需要用到非ASCII字符,我们当然可以去查表得到他们的转义码,但是没有必要.你可以大胆的在代码中这么写,或者加上注释.应为这样很便于阅读. String u

SqlHelper类编写前奏:DataReader关闭链接出现问题

SqlHelper是一个执行数据库操作的助手类,但是当我们没学过DataSet之前,要想使用using搭配SqlConnection和SqlCommand写出一个真正独立的SqlHelper都是不太可能的. 比如:一个常规的ExecuteReader方法如果使用上述做法,代码如下: using System.Data.SqlClient; namespace ExecuteScalar.libs { class SqlHelper { public static SqlDataReader Ex

公共场所英文译写规范

作者: 阮一峰 日期: 2011年1月 5日 随着国际化程度的加深,国内越来越多的公共场所开始提供英文标识. 但是,中国人的英语水平普遍不高,导致大量的翻译错误.比如,下面四个真实的例子,你能看出错在哪里吗?(答案稍后公布) * 停车场:Park * 加油站:Oil Station * 汽车租赁店:Car Rent * 请勿拍照:No Photograph 这种"中式英语"有一个专门的单词,叫做Chinglish.很多老外把它们当做笑话看. 去年,上海召开世博会,为了维护国际大都市的形

写规范的javascript脚本代码 之单var

在函数顶部使用单var语句是比较有用的一种形式,其好处在于: 提供了一个单一的地方去寻找功能所需要的所有局部变量 防止变量在定义之前使用的逻辑错误 帮助你记住声明的全局变量,因此较少了全局变量//zxx:此处我自己是有点晕乎的- 少代码(类型啊传值啊单线完成) 单var形式长得就像下面这个样子: ? 1 2 3 4 5 6 var a=0,       b=3,      c=a+b,     myobject = {},        i,        j; 您可以使用一个var语句声明多个

实体类编写规则

-------------------siwuxie095 实体类编写规则 1.实体类里面的属性是私有的 2.私有属性使用公有的 get 和 set 方法进行操作 3.要求实体类有一个属性作为唯一值(一般使用 id 值) 「实体类要有一个唯一标识的 OID 与表的主键对应」 4.实体类属性建议不使用基本数据类型,而使用基本数据类型对应的包装类 (1)八个基本数据类型对应的包装类 1)int - Integer 2)char - Character 3)其它都是直接首字母变为大写,如:double

Hibernate实体类编写规则

Hibernate 实体类编写规则 1 实体类里面属性私有的   2 私有属性使用公开的set和get方法操作   3 要求实体类有属性作为唯一值(一般使用id值)   4 实体类属性建议不使用基本数据类型,使用基本数据类型对应的包装类 (1)八个基本数据类型对应的包装类 - int - Integer - char - Character - 其他的都是首字母大写 比如 - double - Double - long - Long - short - Short - float - Floa

C++基础学习教程(七)----类编写及类的两个特性解析---&gt;多态&amp;继承

类引入 到目前为止我们所写的自定义类型都是关键字struct,从现在起我们将采用class方式定义类,这种方式对于学习过其他高级语言包括脚本(Such as Python)的人来说再熟悉不过了. 但是在写之前我们还是需要比较一下用struct和class之间有什么区别. 首先对于struct,在C兼容性方面很重要,尽管C++是有别于C的另一门语言,但许多程序还是必须与C交互,C++有两个重要功能,可以方便的与C交互.其中之一的就是POD,即是Plain Old Data(简单旧式数据)的缩写.

C++基础学习教程(六)----类编写的前情回顾以及项目实战(1)

在开始类的编写之前我们依然需要回顾整理一下前面所说的内容,(前面虽然是一个自定义数据类型的实现过程,但是内容有点繁杂). 先看一段代码: /** @file calssStruct.cpp */ /** Member Functions for Class point */ #include <cmath> // for sqrt and atan using namespace std; struct point { point() : x_(0.0), y_(0.0) {} point(d

Django的rest_framework的视图之Mixin类编写视图源码解析

Mixin类编写视图 我们这里用auther表来做演示,先为auther和autherdetail写2个url url(r'^autherdetail/(?P<id>\d+)', views.Book_detail_cbv.as_view(), name="autherdetail"), url(r'^auther/', views.Book_cbv.as_view(),name="auther"), 然后分别为这2个类写对应的序列化的类 class a