Java Setter返回此vs生成器

Java Setter返回此vs生成器,java,builder,Java,Builder,我想知道,当构造一个对象时,setter返回this之间有什么区别吗: 具有ID(字符串名称)的公共用户{ this.name=名称; 归还这个; } 和一个构建器(例如,由IDEA插件生成的构建器) 我的第一印象是,一个二传手返回这个要好得多: 它使用更少的代码-生成器没有额外的类,在对象构造结束时没有build()调用 它读起来更好: newuser().withName(“Some Name”).withAge(30); vs User.UserBuilder.anUserBuild

我想知道,当构造一个对象时,setter返回
this
之间有什么区别吗:

具有ID(字符串名称)的公共用户{
this.name=名称;
归还这个;
}
和一个构建器(例如,由IDEA插件生成的构建器)

我的第一印象是,一个二传手返回
这个
要好得多:

  • 它使用更少的代码-生成器没有额外的类,在对象构造结束时没有
    build()
    调用
  • 它读起来更好:
    newuser().withName(“Some Name”).withAge(30);
    
    vs
    User.UserBuilder.anUserBuilder().withName(“某些名称”).withAge(30.build();
    

那为什么要使用builder呢?有什么我遗漏的吗?

构建器是设计模式,用于为代码提供清晰的结构。它们还经常用于创建不可变的类变量。您还可以在调用
build()
方法时定义前置条件。

这取决于类的性质。如果您的字段不是
final
(即,如果类可以是可变的),则执行以下操作:

new User().setEmail("alalal@gmail.com").setPassword("abcde");
User.newBuilder().withEmail("alalal@gmail.com").withPassowrd("abcde").build();
或者这样做:

new User().setEmail("alalal@gmail.com").setPassword("abcde");
User.newBuilder().withEmail("alalal@gmail.com").withPassowrd("abcde").build();
。。。什么都不会改变

但是,如果您的字段应该是
最终的
(一般来说,这是首选的,以避免对字段进行不必要的修改,当然它们不必是可变的),那么构建器模式可以保证在设置所有字段之前不会构建对象。 当然,您可能会获得相同的结果,即使用所有参数公开单个构造函数:

public User(String email, String password);

。。。但是,当您拥有大量参数时,在构建对象之前,能够看到您所做的每一个设置,会变得更方便、更易读。

我认为您的问题更符合以下公式:

我们是在实现Builder模式时创建一个单独的Builder类,还是继续返回相同的实例

根据报告:

使用生成器模式封装产品的构造 并允许它分步骤构建

因此,封装是重要的一点

现在让我们看看您在原始问题中提供的方法的差异。主要区别在于设计,即如何实现构建器模式,即如何继续构建对象:

  • ObjecBuilder独立类方法中,您不断返回构建器对象,并且在完成构建后,仅返回已完成/构建的对象(!),这是更好地封装创建过程的方法,因为它更一致且结构设计更合理,因为你有一个明显分开的两个不同的阶段:

    1.1)建造对象

    1.2)完成构建并返回构建实例(如果您消除了setter,这可能会使您拥有不可变的构建对象)

  • 在从同一类型返回的示例中,您仍然可以修改它,这可能会导致类的设计不一致和不安全


  • 构建器的一个优点是,您可以使用它来创建对象,而不必知道其精确的类-类似于您可以使用工厂的方式。想象一下,如果您想要创建一个数据库连接,但连接类在MySQL、PostgreSQL、DB2或其他类型之间有所不同,那么构建器就可以选择并实例化正确的实现类,而您实际上不需要担心它


    当然,setter函数不能做到这一点,因为它要求已经实例化一个对象。

    关键是中间对象是否是有效实例

    如果
    new User()
    是有效的
    User
    ,并且
    new User().withName(“Some Name”)
    是有效的
    User
    ,而
    new User().withName(“Some Name”).withAge(30)
    是有效的用户,那么请务必使用您的模式

    但是,如果您没有提供姓名和年龄,
    用户是否真的有效?也许,也许不是:如果这些有一个合理的默认值,可能是这样,但是名称和年龄不能真正有默认值


    关于
    用户.Builder
    的一点是中间结果不是
    用户
    :您设置了多个字段,然后才构建
    用户

    要理解的关键是不可变类型的概念

    假设我有以下代码:

    public class UnitedStates {
        private static final List<String> STATE_NAMES =
            Arrays.asList("Washington", "Ohio", "Oregon", "... etc");
    
        public static List<String> getStateNames() {
            return STATE_NAMES:
        }
    }
    
    这将起作用。现在对于所有的调用方来说,显然有一个状态叫做
    Turtlia
    。华盛顿?什么?无处可寻

    问题是
    数组.asList
    返回一个可变对象:您可以对该对象调用一些方法来更改它

    这样的对象无法与不信任的代码共享,而且由于您不记得自己写过的每一行,一两个月内您无法信任自己,因此,您基本上无法信任任何人。如果要正确编写此代码,只需使用
    List.of
    而不是
    Arrays.asList
    ,因为
    List.of
    生成一个不可变的对象。它有改变它的零方法。它似乎有方法(它有一个
    set
    method!),但试着调用它。它不会起作用,你会得到一个例外,最重要的是,列表不会改变。事实上,这是不可能的。幸运的是,字符串也是不可变的

    不可变的更容易推理,并且可以自由地与您喜欢的任何东西共享,而无需复制

    那么,想要你自己的不可变?很好-但显然唯一的方法是创建一个构造函数,其中所有值都已设置,就是这样-不可变类型不能有
    set
    方法
       newBridge()
            .name("Golden Gate")
            .longestSpan(1280)
            .built(1937)
            .length(2737)
            .build();
    
        Bridge goldenGate = Bridge.create().withName("Golden Gate").withLength(2737);
    
    import lombok.Value;
    import lombok.Builder;
    
    @Value @Builder
    public class Bridge {
        String name;
        int built;
        int length;
        int span;
    }