在java 8下更好地利用枚举-mile米乐体育
java学习
2020年03月25日 23:40
1
在我们的云使用分析api中,返回了格式化过的分析数据(这里指生成分析图)。最近,我们添加了一个特性,允许用户选择时间段(最开始只可以按天选择)。问题是,代码中每天中的时间段部分高度耦合了……
例如,下面这段代码:
private static listcreatelistwithzerosfortimeinterval(datetime from, datetime to, immutableset > metrics) { list points = new arraylist<>(); for (int i = 0; i <= days.daysbetween(from, to).getdays(); i ) { points.add(new datapoint().withdatas(createdataswithzerovalues(metrics)) .withdayofyear(from.withzone(datetimezone.utc) .plusdays(i) .withtimeatstartofday())); } return points; }
注意:days、minutes、hours、weeks 和months一样出现在代码的后面部分。这些代码来自joda-time java时间和日期api。甚至方法的名字都没有反应出(各自的功能)。这些名字牢牢的绑定到了days的概念上。
我也尝试过使用不同时间段方式(比如月、周、小时)。但我看到了糟糕的switch/case鬼鬼祟祟地隐藏在代码里。
你需要知道,switch/case=罪恶 已经深入我心了。在我大学期间的两段实习经历中就已经这么认为了。因此,我会不惜任何代价避免使用switch/case。这主要是因为它们违反了开放闭合原则。我深深地相信,遵循这个原则是写出面向对象代码的最好实践。我不是唯一一个这样想的,robert c. martin曾经说:
在很多方面,开放闭合原则是面向对象设计的核心。遵循这个原则会从面向对象技术中收获巨大的好处,比如可重用性和可维护性1。
我告诉自己:“我们使用java8或许可以发现一些新的特性来避免swtich/case的危险场面出现”。使用java8的新 functions(不是那么新,不过你知道我的意思)。我决定使用枚举代表不同的可得到时间段。
public enum timeperiod { minute(dimension.minute, (from, to) -> minutes.minutesbetween(from, to).getminutes() 1, minutes::minutes, from -> from.withzone(datetimezone.utc) .withsecondofminute(0) .withmillisofsecond(0)), hour(dimension.hour, (from, to) -> hours.hoursbetween(from, to).gethours() 1, hours::hours, from -> from.withzone(datetimezone.utc) .withminuteofhour(0) .withsecondofminute(0) .withmillisofsecond(0)), day(dimension.day, (from, to) -> days.daysbetween(from, to).getdays() 1, days::days, from -> from.withzone(datetimezone.utc) .withtimeatstartofday()), week(dimension.week, (from, to) -> weeks.weeksbetween(from, to).getweeks() 1, weeks::weeks, from -> from.withzone(datetimezone.utc) .withdayofweek(1) .withtimeatstartofday()), month(dimension.month, (from, to) -> months.monthsbetween(from, to).getmonths() 1, months::months, from -> from.withzone(datetimezone.utc) .withdayofmonth(1) .withtimeatstartofday()); private dimensiondimension; private bifunction getnumberofpoints; private function getperiodfromnbofinterval; private function getstartofinterval; private timeperiod(dimension dimension, bifunction getnumberofpoints, function getperiodfromnbofinterval, function getstartofinterval) { this.dimension = dimension; this.getnumberofpoints = getnumberofpoints; this.getperiodfromnbofinterval = getperiodfromnbofinterval; this.getstartofinterval = getstartofinterval; } public dimension getdimension() { return dimension; } public int getnumberofpoints(datetime from, datetime to) { return getnumberofpoints.apply(from, to); } public readableperiod getperiodfromnbofinterval(int nbofinterval) { return getperiodfromnbofinterval.apply(nbofinterval); } public datetime getstartofinterval(datetime from) { return getstartofinterval.apply(from); } }
通过枚举,我就能够很容易地修改代码,允许用户给图表数据点指定时间段。
原来是这样调用:
for (int i = 0; i <= days.daysbetween(from, to).getdays(); i )
变成这样调用:
for (int i = 0; i < timeperiod.getnumberofpoints(from, to); i )
支持getgraphdatapoints调用的usage analytics服务代码已经完成了,并且支持时间段。值得一提的是,它考虑了我之前说过的开放闭合原则。
展开全文