
计算时间偏移量,例如,计算当前时间向前偏移 30 秒的时间,我们利用java.util.Calendar很容易实现。- Calendar cal = Calendar.getInstance();
- cal.setTime(new Date());
- cal.add(Calendar.SECOND, -30);
- System.out.println(cal.getTime());
复制代码
我在进行面试的时候,关于程序设计,有问过应聘者这样的问题。
那么,我们怎么封装这么一个工具类呢?这个工具类提供哪些工具方法呢?每个方法又当怎么实现呢?
下面这段优秀的代码节选自hutool-DateUtil(hutool-all-4.5.18.jar ,maven坐标:cn.hutool:hutool-all:4.5.18),香香的,甜甜的,pretty,graceful,pretty graceful.
坦白说,写出来这个util并不难,你可以写出来你的代码,然后做个比较,看看与优秀代码的差距。- // --------------------------------------------------- Offset for now
- /**
- * 昨天
- *
- * @return 昨天
- */
- public static DateTime yesterday() {
- return offsetDay(new DateTime(), -1);
- }
- /**
- * 明天
- *
- * @return 明天
- * @since 3.0.1
- */
- public static DateTime tomorrow() {
- return offsetDay(new DateTime(), 1);
- }
- /**
- * 上周
- *
- * @return 上周
- */
- public static DateTime lastWeek() {
- return offsetWeek(new DateTime(), -1);
- }
- /**
- * 下周
- *
- * @return 下周
- * @since 3.0.1
- */
- public static DateTime nextWeek() {
- return offsetWeek(new DateTime(), 1);
- }
- /**
- * 上个月
- *
- * @return 上个月
- */
- public static DateTime lastMonth() {
- return offsetMonth(new DateTime(), -1);
- }
- /**
- * 下个月
- *
- * @return 下个月
- * @since 3.0.1
- */
- public static DateTime nextMonth() {
- return offsetMonth(new DateTime(), 1);
- }
- /**
- * 偏移毫秒数
- *
- * @param date 日期
- * @param offset 偏移毫秒数,正数向未来偏移,负数向历史偏移
- * @return 偏移后的日期
- */
- public static DateTime offsetMillisecond(Date date, int offset) {
- return offset(date, DateField.MILLISECOND, offset);
- }
- /**
- * 偏移秒数
- *
- * @param date 日期
- * @param offset 偏移秒数,正数向未来偏移,负数向历史偏移
- * @return 偏移后的日期
- */
- public static DateTime offsetSecond(Date date, int offset) {
- return offset(date, DateField.SECOND, offset);
- }
- /**
- * 偏移分钟
- *
- * @param date 日期
- * @param offset 偏移分钟数,正数向未来偏移,负数向历史偏移
- * @return 偏移后的日期
- */
- public static DateTime offsetMinute(Date date, int offset) {
- return offset(date, DateField.MINUTE, offset);
- }
- /**
- * 偏移小时
- *
- * @param date 日期
- * @param offset 偏移小时数,正数向未来偏移,负数向历史偏移
- * @return 偏移后的日期
- */
- public static DateTime offsetHour(Date date, int offset) {
- return offset(date, DateField.HOUR_OF_DAY, offset);
- }
- /**
- * 偏移天
- *
- * @param date 日期
- * @param offset 偏移天数,正数向未来偏移,负数向历史偏移
- * @return 偏移后的日期
- */
- public static DateTime offsetDay(Date date, int offset) {
- return offset(date, DateField.DAY_OF_YEAR, offset);
- }
- /**
- * 偏移周
- *
- * @param date 日期
- * @param offset 偏移周数,正数向未来偏移,负数向历史偏移
- * @return 偏移后的日期
- */
- public static DateTime offsetWeek(Date date, int offset) {
- return offset(date, DateField.WEEK_OF_YEAR, offset);
- }
- /**
- * 偏移月
- *
- * @param date 日期
- * @param offset 偏移月数,正数向未来偏移,负数向历史偏移
- * @return 偏移后的日期
- */
- public static DateTime offsetMonth(Date date, int offset) {
- return offset(date, DateField.MONTH, offset);
- }
- /**
- * 获取指定日期偏移指定时间后的时间
- *
- * @param date 基准日期
- * @param dateField 偏移的粒度大小(小时、天、月等){@link DateField}
- * @param offset 偏移量,正数为向后偏移,负数为向前偏移
- * @return 偏移后的日期
- */
- public static DateTime offset(Date date, DateField dateField, int offset) {
- Calendar cal = Calendar.getInstance();
- cal.setTime(date);
- cal.add(dateField.getValue(), offset);
- return new DateTime(cal.getTime());
- }
- // ------------------------------------ Offset end ----------------------------------------------
复制代码

为什么说这么代码比较香呢?你品,你细品!
- 易读。注意各个方法尤其是以“offset”开头的方法的签名,包括方法名、方法参数,包括javadoc,相当清晰,易读易理解。另外,这几个方法整体来看,像极了我们母语中的排比句。
- 丰富。按不同的时间单位(如秒、分钟、小时、天、周和月)偏移日期时间值,定义了丰富的方法,各种姿势满足你。
- 易用。除了offsetMinute/offsetSecond等offsetXxx方法,还提供了yesterday / tomorrow / lastWeek / nextWeek / lastMonth /nextMonth等拿来即用的方法,不必再调用offsetXxx。
- 简洁。计算时间偏移量的算法是相同的,所以,这些方法内部均调用一个通用的 offset 方法,该方法使用 DateField 枚举值指定要偏移的时间单位和偏移量。
- 包容。【中途被其他同学打断,一下子断片了。。。】
同样,关于本地缓存工具,分享一段我曾经写的LocalCacheUtil工具类。
  - import cn.hutool.cache.Cache;
- import cn.hutool.cache.CacheUtil;
- import lombok.extern.slf4j.Slf4j;
- import java.util.Collection;
- import java.util.concurrent.TimeUnit;
- import java.util.function.Supplier;
- /**
- * 本地缓存工具
- */
- @Slf4j
- public class LocalCacheUtil {
- private static Cache<String, Object> lfuCache = CacheUtil.newLFUCache(256, TimeUnit.MINUTES.toMillis(30));
- private static Cache<String, Object> timedCache = CacheUtil.newTimedCache(TimeUnit.DAYS.toMillis(1));//过期时间给个默认值
- /**
- * 从本地缓存获取数据。如果没有,则设置。(策略:最少使用原则)
- *
- * @param key
- * @param supplier
- * @param <T>
- * @return
- *
- * @see #lfuCache
- */
- public static <T> T getCache(String key, Supplier<T> supplier) {
- return getCache(key, false, supplier);
- }
- /**
- * 从本地缓存获取数据。如果没有,则设置。(策略:最少使用原则)
- *
- * @param key
- * @param cacheNullOrEmpty 是否缓存null或空集合
- * @param supplier
- * @param <T>
- * @return
- *
- * @see #lfuCache
- */
- public static <T> T getCache(String key, boolean cacheNullOrEmpty, Supplier<T> supplier) {
- return getCache(lfuCache, key, null, cacheNullOrEmpty, supplier);
- }
- /**
- * 获取缓存。如果没有,则设置
- *
- * @param key
- * @param seconds
- * @param supplier 缓存数据提供者
- * @param <T>
- * @return
- */
- public static <T> T getCache(String key, long seconds, Supplier<T> supplier) {
- return getCache(key, seconds, false, supplier);
- }
- /**
- * 删除缓存
- *
- * @param key 缓存key
- */
- public static void removeCache(String key) {
- timedCache.remove(key);
- }
- /**
- * 获取缓存。如果没有,则设置
- *
- * @param key
- * @param seconds
- * @param cacheNullOrEmpty 是否缓存null或空集合
- * @param supplier 缓存数据提供者
- * @param <T>
- * @return
- */
- public static <T> T getCache(String key, long seconds, boolean cacheNullOrEmpty, Supplier<T> supplier) {
- return getCache(timedCache, key, seconds, cacheNullOrEmpty, supplier);
- }
- private static <T> T getCache(Cache<String, Object> myCache, String key, Long seconds, boolean cacheNullOrEmpty, Supplier<T> supplier) {
- if (myCache.containsKey(key)) {
- return (T) myCache.get(key);
- } else {
- T result = supplier.get();
- if (!cacheNullOrEmpty) {
- if (result == null) {
- log.info("设置缓存---value为null,不设置--- key={}", key);
- return null;
- } else if (result instanceof Collection && ((Collection) result).size() == 0) {
- log.info("设置缓存---value是个空集合,不设置--- key={}", key);
- return null;
- }
- }
- log.info("设置缓存 key={}", key);
- if (seconds == null) {
- myCache.put(key, result);
- } else {
- myCache.put(key, result, TimeUnit.SECONDS.toMillis(seconds));
- }
- return result;
- }
- }
- }
复制代码 View Code

另外,在这个DateUtil工具类中,有一个弃用的offsetDate方法如下。- /**
- * 获取指定日期偏移指定时间后的时间
- *
- * @param date 基准日期
- * @param dateField 偏移的粒度大小(小时、天、月等){@link DateField}
- * @param offset 偏移量,正数为向后偏移,负数为向前偏移
- * @return 偏移后的日期
- * @deprecated please use {@link DateUtil#offset(Date, DateField, int)}
- */
- @Deprecated
- public static DateTime offsetDate(Date date, DateField dateField, int offset) {
- return offset(date, dateField, offset);
- }
复制代码 作为一个不断迭代升级的Java工具库,显然hutool不能轻易将之前的方法直接去掉,这会遭到骂娘的。因此,hutool的开发者标记了@Deprecated,并在方法的javadoc里明确指引出来,调用另一个offset(Date, DateField, int)重载。--——————这是一个优秀编码风格,标记弃用,请向使用者描述背景(弃用原因)或告知使用者应该怎么办。
那么,现在,我们来思考一下:为什么弃用这个offsetDate方法改用offset方法呢?欢迎评论区交流!
我在公司内部的软件系统中,一直在践行关于弃用方法的这一优秀编码行为。只是后来,随着开发经验和意识的增强,在行为上做了一些调整。即,我不再一味地标记弃用,而是斩草除根,对于不合理的方法,优秀采用的方式是直接干掉方法并修改对方法的调用,这么做的出发点有三:①我们是中小型内部企业应用系统,工具或组件都是对内使用,具备内部修改的条件;②团队成员编码意识良莠不齐,被明确标记了弃用的方法,有时仍被使用;③最好的方式是一次做好,避免时间一长自己都忘了这些冗余代码了。
The End.
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |