生成器作为(快速失败)状态机

这个想法是几周前在设计“ Generator”类时想到的,该类必须将输入发送给封装的Writer 。 实际上,它是Builder模式。 但是,规则有些复杂,用户必须以某种方式调用add...()方法,才能正确生成输出。

不用说,我不喜欢让一个BuilderImpl类在内部设置和验证各种标志,以便知道什么时候被允许的选择。 解决方案是构建一个有限状态机 ,因为构建者的界面很流畅。 像往常一样,在这篇文章中,我将通过一个例子来说明。

建造者状态机

汤姆和杰瑞(Tom&Jerry)–《老鼠麻烦》,威廉·汉纳(William Hanna)和约瑟夫·巴贝拉(Joseph Barbera)

假设我们要实现一个DateBuilder ,它将以经典的dd.mm.yyyy格式(可能还带有其他类型的分隔符,不仅. )生成一个String 。 为了简单起见,我们将只关注格式,而忽略诸如一个月中的天数,leap年之类的情况。首先是界面:

public interface DateBuilder {

    DateBuilder addDay(final Integer day);
    DateBuilder addMonth(final Integer month);
    DateBuilder addYear(final Integer year);
    DateBuilder addSeparator(final String sep);

    String build();

}

上面的接口将有五个实现: StringDateBuilder (公共入口点), ExpectSeparatorExpectMonthExpectYearExpectBuild (这四个包均受程序包保护,对用户不可见)。 StringDataBuilder看起来像这样:

public final class StringDateBuilder implements DateBuilder {

    private final StringBuilder date = new StringBuilder();

    @Override
    public DateBuilder addDay(final Integer day) {
      this.date.append(String.valueOf(day));
      return new ExpectSeparator(this.date);
    }

    @Override
    public DateBuilder addMonth(final Integer month) {
      throw new UnsupportedOperationException(
        "A day is expected first! Use #addDay!"
      );
    }

    @Override
    public DateBuilder addYear(final Integer year) {
      throw new UnsupportedOperationException(
        "A day is expected first! Use #addDay!"
      );      
    }

    @Override
    public DateBuilder addSeparator(final String sep) {
      throw new UnsupportedOperationException(
        "A day is expected first! Use #addDay!"
      );
    }

    @Override
    public String build() {
      throw new UnsupportedOperationException(
        "Nothing to build yet! Use #addDay!"
      );
    }

}

我相信您已经明白了:其他四个实现将处理它们自己的情况。 例如, ExpectSeparator将从addSeparator(...)之外的所有方法中引发异常,在该方法中,它将分隔符附加到StringBuilder并返回ExpectMonth的实例。 最后,这台机器的最后一个节点将是ExpectBuild (在添加年份之后由ExpectYear返回),它将抛出build()之外所有方法的异常。

这种设计帮助我将代码对象保持较小,没有标志和if/else分支。 与往常一样,上面的每个类都易于测试,并且通过切换返回的实现,可以轻松更改构建器的行为。

当然,我不是唯一想到这些的人:先生。 尼古拉斯·弗兰克(NicolasFränkel)就在上个月在这里写下了这个想法。 但是,我觉得有必要带走我的两分钱,因为我不完全喜欢他的例子:他为构建器的节点使用了不同的接口,以保持构建器的安全性和防白痴性(例如,甚至不允许用户查看addMonthbuild方法(如果他们不应该使用的话)。 我不同意这一点,因为这意味着我需要管理更多的代码,此外,客户端将与构建者的逻辑相结合。 我宁愿只强制用户到学习如何使用生成器(它不应该是他们的一个大的努力,因为他们应该搭上一个最简单的单元测试任何异常,对不对? 吧...

我也找到了这篇文章 ,它提供了更广泛,更理论上的解释,并不一定与Builder模式相关联–如果您考虑一下,这种方法可以用于任何必须根据其内部状态更改其行为的对象。

翻译自: https://www.javacodegeeks.com/2018/12/builder-fail-fast-state-machine.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值