Greeter函数的greet类,该类接收name作为输入并输出Hello <name>。greet的签名不应在整个kata中发生变化。您可以随意构造Greeter对象。greet裁剪输入greet将名称的第一个字母大写greet在时间为06:00-12:00时返回Good morning <name>。greet在时间为18:00-22:00时返回Good evening <name>。greet在时间为22:00-06:00时返回Good night <name>。对于这个卡塔,我提供了以下代码。
public class Greeter {
private final TimeProvider timeProvider;
public Greeter() {
timeProvider = LocalTime::now;
}
public Greeter(TimeProvider timeProvider) {
this.timeProvider = timeProvider;
}
public String greet(String name) {
String beginning = getGreetingBeginning();
String correctedName = getCorrectedName(name);
return String.format("%s %s", beginning, correctedName);
}
private String getGreetingBeginning() {
if (isMorning())
return "Good morning";
if (isEvening())
return "Good evening";
if (isNight())
return "Good night";
return "Hello";
}
private boolean isMorning() {
LocalTime actualTime = timeProvider.provide();
return actualTime.isAfter(LocalTime.parse("06:00:00"))
&& actualTime.isBefore(LocalTime.parse("12:00:01"));
}
private boolean isEvening() {
LocalTime actualTime = timeProvider.provide();
return actualTime.isAfter(LocalTime.parse("18:00:00"))
&& actualTime.isBefore(LocalTime.parse("22:00:01"));
}
private boolean isNight() {
LocalTime actualTime = timeProvider.provide();
boolean isAfterEvening = actualTime.isAfter(LocalTime.parse("22:00:00"));
boolean beforeMidnight = actualTime.isBefore(LocalTime.parse("23:59:59"));
boolean isJustBeforeMidnight = actualTime.equals(LocalTime.parse("23:59:59"));
boolean isMidnight = actualTime.equals(LocalTime.parse("00:00:00"));
boolean isAfterMidnight = actualTime.isAfter(LocalTime.parse("00:00:00"));
boolean beforeMorning = actualTime.isBefore(LocalTime.parse("06:00:01"));
return
(isAfterEvening && beforeMidnight)
|| isJustBeforeMidnight
|| isMidnight
|| (isAfterMidnight && beforeMorning);
}
private String getCorrectedName(String name) {
String trimmedName = name.trim();
return StringUtils.capitalize(trimmedName);
}
}interface TimeProvider {
LocalTime provide();
}重构
代码
在此之后,我认为Greeter做了很多事情,所以我把它分成多个小类。
public class Greeter {
private final BeginningProvider beginningProvider;
public Greeter(BeginningProvider beginningProvider) {
this.beginningProvider = beginningProvider;
}
public String greet(String name) {
String beginning = beginningProvider.provide();
String correctedName = getCorrectedName(name);
return String.format("%s %s", beginning, correctedName);
}
private String getCorrectedName(String name) {
String trimmedName = name.trim();
return StringUtils.capitalize(trimmedName);
}
}interface BeginningProvider {
String provide();
}class BeginningProviderImpl implements BeginningProvider {
private final TimeProvider timeProvider;
private final List<TimeRangePredicateSupplier> timeRanges = List.of(
new MorningPredicateSupplier(),
new EveningPredicateSupplier(),
new NightPredicateSupplier()
);
BeginningProviderImpl() {
timeProvider = LocalTime::now;
}
BeginningProviderImpl(TimeProvider timeProvider) {
this.timeProvider = timeProvider;
}
@Override
public String provide() {
LocalTime time = timeProvider.provide();
for (TimeRangePredicateSupplier predicateSupplier : timeRanges)
if (predicateSupplier.test(time))
return predicateSupplier.get();
return new AfternoonPredicateSupplier().get();
}
}interface TimeProvider {
LocalTime provide();
}interface TimeRangePredicateSupplier {
boolean test(LocalTime time);
String get();
}class MorningPredicateSupplier implements TimeRangePredicateSupplier {
@Override
public boolean test(LocalTime time) {
return time.isAfter(LocalTime.parse("06:00:00"))
&& time.isBefore(LocalTime.parse("12:00:01"));
}
@Override
public String get() {
return "Good morning";
}
}class EveningPredicateSupplier implements TimeRangePredicateSupplier {
@Override
public boolean test(LocalTime time) {
return time.isAfter(LocalTime.parse("18:00:00"))
&& time.isBefore(LocalTime.parse("22:00:01"));
}
@Override
public String get() {
return "Good evening";
}
}class NightPredicateSupplier implements TimeRangePredicateSupplier {
@Override
public boolean test(LocalTime time) {
boolean isAfterEvening = time.isAfter(LocalTime.parse("22:00:00"));
boolean beforeMidnight = time.isBefore(LocalTime.parse("23:59:59"));
boolean isJustBeforeMidnight = time.equals(LocalTime.parse("23:59:59"));
boolean isMidnight = time.equals(LocalTime.parse("00:00:00"));
boolean isAfterMidnight = time.isAfter(LocalTime.parse("00:00:00"));
boolean beforeMorning = time.isBefore(LocalTime.parse("06:00:01"));
return
(isAfterEvening && beforeMidnight)
|| isJustBeforeMidnight
|| isMidnight
|| (isAfterMidnight && beforeMorning);
}
@Override
public String get() {
return "Good night";
}
}class AfternoonPredicateSupplier implements TimeRangePredicateSupplier {
@Override
public boolean test(LocalTime time) {
throw new AfternoonCheckException("Afternoon is default case, shouldn't be checked");
}
@Override
public String get() {
return "Hello";
}
}class AfternoonCheckException extends RuntimeException {
AfternoonCheckException(String s) {
super(s);
}
}以上哪个版本更容易阅读,如果您刚刚加入项目,您希望看到哪个版本?
发布于 2021-05-17 15:01:13
在原始代码中,您多次调用timeProvider.provide(),在每次应该进行相同时间比较的情况下,会导致(稍微)不同的次数。虽然在这种特殊情况下,这可能不会有问题,但可能会导致很难跟踪bug。
然后,在这两种情况下,你都考虑过了时间的比较。LocalTime的高分辨率(理论上)是纳秒。这导致了isNight比较中的一秒间隔。您可以通过使用.truncatedTo(ChronoUnit.SECONDS)来解决这个问题,但这是不必要的,特别是整个午夜的事情。只是使用
time.isAfter(LocalTime.parse("22:00:00")) || time.isBefore(LocalTime.parse("06:00:00"));会很好的。您也不需要在其他时间范围的末尾再加一秒钟。
如果您想要非常肯定地说,夜间范围和夜间范围之间没有(理论上的)差距,请添加
|| time.equals(LocalTime.parse("22:00:00"))一个比较表达式。
顺便说一句,所有的LocalTime.parses都是在每次通话中执行的。最好使用常量来存储这些值。
这个练习想教的一件事是使代码易于扩展。目前,您已经硬编码的时间范围和问候。使用TimeRangePredicateSupplier的第二个解决方案是一个很好的开端,但是实际的实现仍然是在BeginningProviderImpl中硬编码的。
对于第一个解决方案,尝试使用以下构造函数实现它:
public Greeter(TimeProvider timeProvider, List<TimeRangeGreeting> timeRangeGreetings, String defaultGreeting) {
// ...
}其中TimeRangeGreeting是一个数据类,如
public class TimeRangeGreeting {
private final LocalTime start;
private final LocalTime end;
private final String greeting;
// TODO constructor, getters
}或者,如果您使用的是Java 14,则为record:
public record TimeRangeGreeting(LocalTime start, LocalTime end, String greeting) {}https://codereview.stackexchange.com/questions/260841
复制相似问题