計算時間偏移量,例如,計算當前時間向前偏移 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 枚舉值指定要偏移的時間單位和偏移量。
- 包容。注意最後那個public的
offset(Date date, DateField dateField, int offset)
方法,當上面的計算時間偏移量的方法無法滿足使用要求時(當然,已經是應有盡有了),你就可以用它了。
同樣,關於本地緩存工具,分享一段我曾經寫的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; } } }
另外,在這個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.