这个 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中的新窗口中打开文件

    我创建了一个 JFrame 并放置了一个文本字段和按钮 在文本字段中我放置了从文本文件读取的名称 我知道我想单击按钮并打开一个已知窗口 我想在其中放置名称 其他信息来自同一个文件 这是我的代码 这是我的主框架 package Fronten
  • java.lang.ClassNotFoundException:javax.mail.MessagingException

    我想使用 eclipse 将电子邮件从我的 gmail 帐户发送到另一个邮件帐户 我使用 apache tomcat 7 0 34 作为我的 Web 服务器 并使用端口 8080 作为 apache 服务器 HTTP 1 1 并使用 JRE
  • 如何在java中将数组值排序为循环格式?

    我的数组值如下 String value 1 2 3 4 5 6 7 8 9 10 假设如果我将值 5 传递给 tat 数组 它应该按如下顺序排序 5 6 7 8 9 10 1 2 3 4 怎么办 有人帮忙吗 感谢你 你需要的就是所谓的轮换
  • eclipse行号状态行贡献项是如何实现的?

    我需要更新状态行编辑器特定的信息 我已经有了自己的实现 但我想看看 eclipse 贡献项是如何实现的 它显示状态行中的行号 列位置 谁能指点一下 哪里可以找到源代码 提前致谢 亚历克斯 G 我一直在研究它 它非常复杂 我不确定我是否了解完
  • Android studio - 如何保存先前活动中选择的数据

    这是我的代码片段 这Textview充当按钮并具有Onclicklistner在他们 当cpu1000时Textview单击它会导致cpu g1000其代码如下所示的类 public class Game 1000 extends AppC
  • 从 MATLAB 调用 Java?

    我想要Matlab程序调用java文件 最好有一个例子 需要考虑三种情况 Java 内置库 也就是说 任何描述的here http docs oracle com javase 6 docs api 这些项目可以直接调用 例如 map ja
  • Mockito 使用 @Mock 时将 Null 值注入到 Spring bean 中?

    由于我是 Spring Test MVC 的新手 我不明白这个问题 我从以下代码中获取了http markchensblog blogspot in search label Spring http markchensblog blogsp
  • Java 中如何将 char 转换为 int? [复制]

    这个问题在这里已经有答案了 我是Java编程新手 我有例如 char x 9 我需要得到撇号中的数字 即数字 9 本身 我尝试执行以下操作 char x 9 int y int x 但没有成功 那么我应该怎么做才能得到撇号中的数字呢 ASC
  • 将非 Android 项目添加到 Android 项目

    我在 Eclipse 中有三个项目 Base Server 和 AndroidClient Base和Server是Java 1 7项目 而AndroidClient显然是一个android项目 基础项目具有在服务器和 Android 客户
  • Akka 与现有 java 项目集成的示例

    如果我已经有现有的javaWeb 应用程序使用spring and servlet容器 将 Akka 集成到其中的正确方法是什么 就像我将会有Actor1 and Actor2互相沟通的 开始使用这些演员的切入点是什么 例如 1 把它放在那
  • 如何在.NET中使用java.util.zip.Deflater解压缩放气流?

    之后我有一个转储java util zip Deflater 可以确认它是有效的 因为 Java 的Inflater打开它很好 并且需要在 NET中打开它 byte content ReadSample sampleName var inp
  • 提高 PostgreSQL 1 亿数据左连接查询性能

    我在用Postgresql 9 2 version Windows 7 64 bit RAM 6GB 这是一个Java企业项目 我必须在我的页面中显示订单相关信息 有三个表通过左连接连接在一起 Tables TV HD 389772 行 T
  • Java继承,扩展类如何影响实际类

    我正在查看 Sun 认证学习指南 其中有一段描述了最终修饰符 它说 如果程序员可以自由地扩展我们所知的 String 类文明 它可能会崩溃 他什么意思 如果可以扩展 String 类 我是否不会有一个名为 MyString 的类继承所有 S
  • 将 JavaFX FXML 对象分组在一起

    非常具有描述性和信息性的答案将从我这里获得价值 50 声望的赏金 我正在 JavaFX 中开发一个应用程序 对于视图 我使用 FXML
  • 使用 HtmlUnit 定位弹出窗口

    我正在构建一个登录网站并抓取一些数据的程序 登录表单是一个弹出窗口 所以我需要访问这个www betexplorer com网站 在页面的右上角有一个登录链接 写着 登录 我单击该链接 然后出现登录弹出表单 我能够找到顶部的登录链接 但找不
  • 将 JScrollPane 添加到 JFrame

    我有一个关于向 Java 框架添加组件的问题 我有一个带有两个按钮的 JPanel 和一个添加了 JTable 的 JScrollPane 我想将这两个添加到 JFrame 中 我可以将 JPanel 添加到 JFrame 或将 JScro
  • Android S8+ 警告消息“不支持当前的显示尺寸设置,可能会出现意外行为”

    我在 Samsung S8 Android 7 中收到此警告消息 APP NAME 不支持当前的显示尺寸设置 可能会 行为出乎意料 它意味着什么以及如何删除它 谢谢 通过添加解决supports screens 机器人 xlargeScre
  • 列表过滤器内的 Java 8 lambda 列表

    示例 JSON id 1 products id 333 status Active id 222 status Inactive id 111 status Active id 2 products id 6 status Active
  • Log4j2 ThreadContext 映射不适用于parallelStream()

    我有以下示例代码 public class Test static System setProperty isThreadContextMapInheritable true private static final Logger LOGG
  • Spring RESTful控制器方法改进建议

    我是 Spring REST 和 Hibernate 的新手 也就是说 我尝试组合一个企业级控制器方法 我计划将其用作未来开发的模式 您认为可以通过哪些方法来改进 我确信有很多 RequestMapping value user metho

随机推荐