首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >将Greeter类拆分为多个小类

将Greeter类拆分为多个小类
EN

Code Review用户
提问于 2021-05-17 08:33:58
回答 1查看 274关注 0票数 5

  1. 编写一个具有Greeter函数的greet类,该类接收name作为输入并输出Hello <name>greet的签名不应在整个kata中发生变化。您可以随意构造Greeter对象。
  2. greet裁剪输入
  3. greet将名称的第一个字母大写
  4. greet在时间为06:00-12:00时返回Good morning <name>
  5. greet在时间为18:00-22:00时返回Good evening <name>
  6. greet在时间为22:00-06:00时返回Good night <name>

原始代码

对于这个卡塔,我提供了以下代码。

代码语言:javascript
复制
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);
    }

}
代码语言:javascript
复制
interface TimeProvider {
    LocalTime provide();
}

重构

后的

代码

在此之后,我认为Greeter做了很多事情,所以我把它分成多个小类。

代码语言:javascript
复制
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);
    }
}
代码语言:javascript
复制
interface BeginningProvider {
    String provide();
}
代码语言:javascript
复制
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();
    }
}
代码语言:javascript
复制
interface TimeProvider {
    LocalTime provide();
}
代码语言:javascript
复制
interface TimeRangePredicateSupplier {
    boolean test(LocalTime time);
    String get();
}
代码语言:javascript
复制
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";
    }
}
代码语言:javascript
复制
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";
    }
}
代码语言:javascript
复制
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";
    }
}
代码语言:javascript
复制
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";
    }
}
代码语言:javascript
复制
class AfternoonCheckException extends RuntimeException {
    AfternoonCheckException(String s) {
        super(s);
    }
}

问题

以上哪个版本更容易阅读,如果您刚刚加入项目,您希望看到哪个版本?

EN

回答 1

Code Review用户

发布于 2021-05-17 15:01:13

在原始代码中,您多次调用timeProvider.provide(),在每次应该进行相同时间比较的情况下,会导致(稍微)不同的次数。虽然在这种特殊情况下,这可能不会有问题,但可能会导致很难跟踪bug。

然后,在这两种情况下,你都考虑过了时间的比较。LocalTime的高分辨率(理论上)是纳秒。这导致了isNight比较中的一秒间隔。您可以通过使用.truncatedTo(ChronoUnit.SECONDS)来解决这个问题,但这是不必要的,特别是整个午夜的事情。只是使用

代码语言:javascript
复制
time.isAfter(LocalTime.parse("22:00:00")) || time.isBefore(LocalTime.parse("06:00:00"));

会很好的。您也不需要在其他时间范围的末尾再加一秒钟。

如果您想要非常肯定地说,夜间范围和夜间范围之间没有(理论上的)差距,请添加

代码语言:javascript
复制
|| time.equals(LocalTime.parse("22:00:00"))

一个比较表达式。

顺便说一句,所有的LocalTime.parses都是在每次通话中执行的。最好使用常量来存储这些值。

这个练习想教的一件事是使代码易于扩展。目前,您已经硬编码的时间范围和问候。使用TimeRangePredicateSupplier的第二个解决方案是一个很好的开端,但是实际的实现仍然是在BeginningProviderImpl中硬编码的。

对于第一个解决方案,尝试使用以下构造函数实现它:

代码语言:javascript
复制
public Greeter(TimeProvider timeProvider, List<TimeRangeGreeting> timeRangeGreetings, String defaultGreeting) {
    // ...
}

其中TimeRangeGreeting是一个数据类,如

代码语言:javascript
复制
public class TimeRangeGreeting {
   private final LocalTime start;
   private final LocalTime end;
   private final String greeting;

   // TODO constructor, getters
}

或者,如果您使用的是Java 14,则为record

代码语言:javascript
复制
public record TimeRangeGreeting(LocalTime start, LocalTime end, String greeting) {}
票数 4
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/260841

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档