这个 Monster Builder 是一个很好的 Builder / Factory 模式,用于抽象与 setter 混合的长构造函数吗?

2024-01-03

这是一个关于组合的人机界面问题步骤生成器模式 http://rdafbn.blogspot.co.uk/2012/07/step-builder-pattern_28.htmlenhanced https://stackoverflow.com/questions/1638722/how-to-improve-the-builder-pattern or wizard http://www.javacodegeeks.com/2012/01/wizard-design-pattern.html构建器模式转化为创造的 https://en.wikipedia.org/wiki/Creational_pattern DSL https://en.wikipedia.org/wiki/Domain-specific_language。它使用类似流式的接口,尽管它使用方法链接,而不是级联。也就是说,这些方法返回不同的类型。

我遇到一个怪物类,它有两个构造函数,混合使用整数、字符串和字符串数组。每个构造函数有 10 个参数长。它还具有大约 40 个可选设置器;如果一起使用,其中一些会相互冲突。它的构造代码看起来像这样:

Person person = Person("Homer","Jay", "Simpson","Homie", null, "black", "brown", 
  new Date(1), 3, "Homer Thompson", "Pie Man", "Max Power", "El Homo", 
  "Thad Supersperm", "Bald Mommy", "Rock Strongo", "Lance Uppercut", "Mr. Plow");

person.setClothing("Pants!!");     
person.setFavoriteBeer("Duff");
person.setJobTitle("Safety Inspector");

这最终失败了,因为事实证明两者都设置了最喜欢的啤酒 and 职称不兼容。叹。

重新设计怪物类别不是一个选择。它被广泛使用。有用。我只是不想再看着它被直接建造了。我想写一些干净的东西来满足它。会遵循其规则而不需要开发人员记住它们的东西。

与我一直在研究的精彩构建器模式相反,这个东西没有风格或类别。它始终需要一些字段,需要时需要其他字段,而有些字段仅取决于之前设置的内容。构造函数不可伸缩。他们提供了两种替代方法来使类进入相同的状态。它们又长又丑。他们想要吃的东西各不相同。

流畅的构建器肯定会让长的构建器更容易查看。然而,大量的可选设置器使所需的设置器变得混乱。并且级联流畅构建器无法满足一个要求:编译时强制。

构造函数强制开发人员显式添加必填字段,即使将其清空也是如此。当使用级联流畅构建器时,这一点就会丢失。就像 setter 丢失一样。我想要一种方法来阻止开发人员在添加每个必填字段之前进行构建。

与许多构建器模式不同,我追求的不是不变性。我要离开课堂,就像我发现的那样。我想通过查看构建对象的代码来了解构建的对象是否处于良好状态。无需参考文档。这意味着它需要引导程序员完成有条件要求的步骤。

Person makeHomer(PersonBuilder personBuilder){ //Injection avoids hardcoding implementation
    return personBuilder

         // -- These have good default values, may be skipped, and don't conflict -- //
        .doOptional()
            .addClothing("Pants!!")   //Could also call addTattoo() and 36 others

         // -- All fields that always must be set.  @NotNull might be handy. -- //
        .doRequired()                 //Forced to call the following in order
            .addFirstName("Homer")
            .addMiddleName("Jay")
            .addLastName("Simpson")
            .addNickName("Homie")
            .addMaidenName(null)      //Forced to explicitly set null, a good thing
            .addEyeColor("black")
            .addHairColor("brown")
            .addDateOfBirth(new Date(1))
            .addAliases(
                "Homer Thompson",
                "Pie Man",
                "Max Power",
                "El Homo",
                "Thad Supersperm",
                "Bald Mommy",
                "Rock Strongo",
                "Lance Uppercut",
                "Mr. Plow")

         // -- Controls alternatives for setters and the choice of constructors -- //
        .doAlternatives()           //Either x or y. a, b, or c. etc.
            .addBeersToday(3)       //Now can't call addHowDrunk("Hammered"); 
            .addFavoriteBeer("Duff")//Now can’t call addJobTitle("Safety Inspector");  

        .doBuild()                  //Not available until now
    ;
}   

Person 可以在 addBeersToday() 之后构建,因为此时所有构造函数信息都是已知的,但直到 doBuild() 才会返回。

public Person(String firstName, String middleName, String lastName,
               String nickName, String maidenName, String eyeColor, 
               String hairColor, Date dateOfBirth, int beersToday, 
               String[] aliases);

public Person(String firstName, String middleName, String lastName,
               String nickName, String maidenName, String eyeColor, 
               String hairColor, Date dateOfBirth, String howDrunk,
               String[] aliases);

这些参数设置的字段绝不能保留默认值。 beersToday 和 howDrunk 在同一领域设置不同的方式。 favoriteBeer 和 jobTitle 是不同的字段,但会与类的使用方式发生冲突,因此只应设置一个字段。它们是通过 setter 而不是构造函数来处理的。

The doBuild()方法返回一个Person目的。这是唯一一个这样做的Person是它将返回的唯一类型。当它发生时Person已完全初始化。

在接口的每个步骤中,返回的类型并不总是相同的。更改类型是指导开发人员完成这些步骤的方式。它只提供有效的方法。这doBuild()在完成所有必需的步骤之前,该方法不可用。

do/add 前缀是一个使编写更容易的拼凑,因为改变返回类型 与作业不匹配并使智能感知建议变成按字母顺序排列 在日食中。我已经确认intellij没有这个问题。谢谢尼姆奇普斯基。

这个问题是关于接口的,所以我会接受不提供实现的答案。但如果你知道的话,请分享。

如果您建议替代模式,请显示其正在使用的界面。使用示例中的所有输入。

如果您建议使用此处提供的界面或一些细微的变化,请捍卫它免受批评,例如this http://ocramius.github.io/blog/fluent-interfaces-are-evil/.

我真正想知道的是大多数人是否更愿意使用这个界面来构建或其他一些。这是人机界面问题。这是否违反PoLA https://en.wikipedia.org/wiki/Principle_of_least_astonishment?不要担心实施起来有多困难。

但是,如果您对实现感到好奇:

失败的尝试 http://java.dzone.com/articles/dive-builder-pattern(没有足够的状态或了解有效与非默认)

步骤生成器实施 http://rdafbn.blogspot.co.uk/2012/07/step-builder-pattern_28.html(对于多个构造函数或替代方案不够灵活)

增强的构建器 https://stackoverflow.com/questions/1638722/how-to-improve-the-builder-pattern(仍然是线性的,但具有灵活的状态)

向导生成器 http://www.javacodegeeks.com/2012/01/wizard-design-pattern.html(处理分叉但不记得选择构造函数的路径)

要求:

  • 怪物(人)类已经禁止修改和扩展;不敏感

Goals:

  • 隐藏长构造函数,因为 Monster 类有 10 个必需参数
  • 根据使用的替代方案确定要调用哪个构造函数
  • 禁止冲突的设置者
  • 在编译时强制执行规则

Intent:

  • 当默认值不可接受时明确发出信号

一个静态内部构建器,因 josh bloch 在 effective java 中而闻名。

必需参数是构造函数参数,可选参数是方法。

一个例子。仅需要用户名的调用:

RegisterUserDto myDto = RegisterUserDto.Builder(myUsername).password(mypassword).email(myemail).Build();

和底层代码(省略明显的实例变量):

private RegisterUserDTO(final Builder builder) {
        super();
        this.username = builder.username;
        this.firstName = builder.firstName;
        this.surname = builder.surname;
        this.password = builder.password;
        this.confirmPassword = builder.confirmPassword;
    }


    public static class Builder {
        private final String username;

        private String firstName;

        private String surname;

        private String password;

        private String confirmPassword;

        public Builder(final String username) {
            super();
            this.username = username;
        }

        public Builder firstname(final String firstName) {
            this.firstName = firstName;
            return this;
        }

        public Builder surname(final String surname) {
            this.surname = surname;
            return this;
        }

        public Builder password(final String password) {
            this.password = password;
            return this;
        }

        public Builder confirmPassword(final String confirmPassword) {
            this.confirmPassword = confirmPassword;
            return this;
        }

        public RegisterUserDTO build() {
            return new RegisterUserDTO(this);
        }
    }
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

这个 Monster Builder 是一个很好的 Builder / Factory 模式,用于抽象与 setter 混合的长构造函数吗? 的相关文章

  • Java 中等效的并行扩展

    我在 Net 开发中使用并行扩展有一些经验 但我正在考虑在 Java 中做一些工作 这些工作将受益于易于使用的并行库 JVM 是否提供任何与并行扩展类似的工具 您应该熟悉java util concurrent http java sun
  • java.lang.NoClassDefFoundError:org.apache.batik.dom.svg.SVGDOMImplementation

    我在链接到我的 Android LibGDX 项目的 Apache Batik 库时遇到了奇怪的问题 但让我们从头开始 在 IntelliJ Idea 中我有一个项目 其中包含三个模块 Main Android 和 Desktop 我强调的
  • 在画布上绘图

    我正在编写一个 Android 应用程序 它可以在视图的 onDraw 事件上直接绘制到画布上 我正在绘制一些涉及单独绘制每个像素的东西 为此我使用类似的东西 for int x 0 x lt xMax x for int y 0 y lt
  • Play框架运行应用程序问题

    每当我尝试运行使用以下命令创建的新 Web 应用程序时 我都会收到以下错误Play http www playframework org Error occurred during initialization of VM Could no
  • 在 HTTPResponse Android 中跟踪重定向

    我需要遵循 HTTPost 给我的重定向 当我发出 HTTP post 并尝试读取响应时 我得到重定向页面 html 我怎样才能解决这个问题 代码 public void parseDoc final HttpParams params n
  • Android MediaExtractor seek() 对 MP3 音频文件的准确性

    我在使用 Android 时无法在eek 上获得合理的准确度MediaExtractor 对于某些文件 例如this one http www archive org download emma solo librivox emma 01
  • 列出jshell中所有活动的方法

    是否有任何命令可以打印当前 jshell 会话中所有新创建的方法 类似的东西 list但仅适用于方法 您正在寻找命令 methods all 它会打印所有方法 包括启动 JShell 时添加的方法 以及失败 被覆盖或删除的方法 对于您声明的
  • 反射找不到对象子类型

    我试图通过使用反射来获取包中的所有类 当我使用具体类的代码 本例中为 A 时 它可以工作并打印子类信息 B 扩展 A 因此它打印 B 信息 但是当我将它与对象类一起使用时 它不起作用 我该如何修复它 这段代码的工作原理 Reflection
  • Spring Data JPA 应用排序、分页以及 where 子句

    我目前正在使用 Spring JPA 并利用此处所述的排序和分页 如何通过Spring data JPA通过排序和可分页查询数据 https stackoverflow com questions 10527124 how to query
  • 十进制到八进制的转换[重复]

    这个问题在这里已经有答案了 可能的重复 十进制转换错误 https stackoverflow com questions 13142977 decimal conversion error 我正在为一个类编写一个程序 并且在计算如何将八进
  • 从 127.0.0.1 到 2130706433,然后再返回

    使用标准 Java 库 从 IPV4 地址的点分字符串表示形式获取的最快方法是什么 127 0 0 1 到等效的整数表示 2130706433 相应地 反转所述操作的最快方法是什么 从整数开始2130706433到字符串表示形式 127 0
  • 如何在控制器、服务和存储库模式中使用 DTO

    我正在遵循控制器 服务和存储库模式 我只是想知道 DTO 在哪里出现 控制器应该只接收 DTO 吗 我的理解是您不希望外界了解底层域模型 从领域模型到 DTO 的转换应该发生在控制器层还是服务层 在今天使用 Spring MVC 和交互式
  • Eclipse Java 远程调试器通过 VPN 速度极慢

    我有时被迫离开办公室工作 这意味着我需要通过 VPN 进入我的实验室 我注意到在这种情况下使用 Eclipse 进行远程调试速度非常慢 速度慢到调试器需要 5 7 分钟才能连接到远程 jvm 连接后 每次单步执行断点 行可能需要 20 30
  • Google App Engine 如何预编译 Java?

    App Engine 对应用程序的 Java 字节码使用 预编译 过程 以增强应用程序在 Java 运行时环境中的性能 预编译代码的功能与原始字节码相同 有没有详细的信息这是做什么的 我在一个中找到了这个谷歌群组消息 http groups
  • 无法捆绑适用于 Mac 的 Java 应用程序 1.8

    我正在尝试将我的 Java 应用程序导出到 Mac 该应用程序基于编译器合规级别 1 7 我尝试了不同的方法来捆绑应用程序 1 日食 我可以用来在 Eclipse 上导出的最新 JVM 版本是 1 6 2 马文 看来Maven上也存在同样的
  • 如何从泛型类调用静态方法?

    我有一个包含静态创建方法的类 public class TestClass public static
  • 静态变量的线程安全

    class ABC implements Runnable private static int a private static int b public void run 我有一个如上所述的 Java 类 我有这个类的多个线程 在里面r
  • 使用 JMF 创建 RTP 流时出现问题

    我正处于一个项目的早期阶段 需要使用 RTP 广播DataStream创建自MediaLocation 我正在遵循一些示例代码 该代码目前在rptManager initalize localAddress 出现错误 无法打开本地数据端口
  • java.lang.IllegalStateException:驱动程序可执行文件的路径必须由 webdriver.chrome.driver 系统属性设置 - Similiar 不回答

    尝试学习 Selenium 我打开了类似的问题 但似乎没有任何帮助 我的代码 package seleniumPractice import org openqa selenium WebDriver import org openqa s
  • 节拍匹配算法

    我最近开始尝试创建一个移动应用程序 iOS Android 它将自动击败比赛 http en wikipedia org wiki Beatmatching http en wikipedia org wiki Beatmatching 两

随机推荐