一道面试题:计算时间偏移量,怎么设计你的程序?
标签: 一道面试题:计算时间偏移量,怎么设计你的程序? 博客 51CTO博客
2023-07-23 18:24:16 122浏览
下面这段优秀的代码节选自hutool-DateUtil(hutool-all-4.5.18.jar ,maven坐标:cn.hutool:hutool-all:4.5.18),香香的,甜甜的!
计算时间偏移量,例如,计算当前时间向前偏移 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;
}
}
}
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.
好博客就要一起分享哦!分享海报
此处可发布评论
评论(0)展开评论
展开评论