【问题标题】:Design: Java and returning self-reference in setter methods [closed]设计:Java 和在 setter 方法中返回自引用 [关闭]
【发布时间】:2010-09-07 02:11:26
【问题描述】:

对于具有一长串经常使用的 setter 的类,我发现这种方式非常有用(尽管我最近阅读了 Effective Java 中的 Builder pattern 有点相同) .

基本上,所有 setter 方法都返回对象本身,因此您可以使用如下代码:

myClass
    .setInt(1)
    .setString("test")
    .setBoolean(true);

Setter 最后简单地返回这个:

public MyClass setInt(int anInt) {
    // [snip]
    return this;
}

你的意见是什么?优缺点都有什么?这对性能有影响吗?

在 c++ 中也称为named parameter idiom

【问题讨论】:

  • 就像 Joshua Bloch 的书,Effective Java (2nd edition),Builder Pattern 的优点之一,你可以更容易地推断出对象的状态,因为一旦它被创建,它不能变异。它还为类的客户端提供了更流畅的api,尤其是当类的参数很多时,其中许多是可选的。
  • 看起来这在 C++ 中被称为命名参数习语。我通常赞成它而不是具有许多参数的传统构造函数。见stackoverflow.com/questions/752762/…cs.technion.ac.il/users/yechiel/c++-faq/…

标签: java


【解决方案1】:

@pek
链式调用是 Java 7 的提议之一。它说如果方法返回类型为 void,它应该隐式返回 this。如果你对这个话题感兴趣,Alex Miller's Java 7 page 上有一堆链接和一个简单的例子。

【讨论】:

  • 我发现这个成语可能是一个好主意(即使我自己不会使用它),但是如果可以通过显式声明 setter 的返回类型来完成,为什么要扩展语言非无效?我不喜欢对一种语言进行临时扩展以支持特定习语的想法。
  • 我同意@Giorgio。这是“魔术”行为,所有魔术都是坏的。从某种意义上说,这很神奇,您无法检查源以找出它返回的内容,您必须知道 void 返回一个自我引用......我宁愿他们为此引入一个新的返回类型...称它为“自我”而不是“无效”。什么的。
  • @Tor Valamo:我同意你的看法。除此之外,我认为编程语言应该提供概念而不是习语。如果一个人真的想使用许多习语,应该使用像 Lisp 这样的可扩展语言,但不能指望一种语言会不断扩展以支持所有可能的习语:它们太多了,以至于一种语言很容易(不必要地)变得复杂.
  • 将它构建到语言中的原因是,当您想要返回最多派生类型的可链接对象的层次结构时,它会变得很痛苦。目前,您必须拥有<Self> 泛型以允许子类更改返回类型,因此调用方法不会失去它们正在调用的对象的范围。构建到 jvm 中消除了这种丑陋。另外,它可以根据需要将对此的引用压入堆栈,而不是返回语句; “按需退货”而不是“始终退货”,在您不使用时很难优化。
【解决方案2】:

这称为Fluent Interface,供参考。

就个人而言,我认为这是一个非常巧妙的想法,但确实是一个品味问题。我认为jQuery 是这样工作的。

【讨论】:

    【解决方案3】:

    我自己不会这样做,因为对我来说它混淆了特定方法的作用,并且方法链接对我的用处有限,而不是直接使用它。不过,这不会让我陷入愤怒和精神病的颤抖之球,这总是一件好事。 :')

    我不会担心性能;问问 Knuth。

    【讨论】:

    • 有时我看到这些怪物大小的构造函数,我想到的是如果他们做了一些参数链接,代码会更容易理解。我更喜欢改进的可读性。当然,如果这一切都通过 vanilla setter 在单独的行上完成,那也不会那么糟糕。
    【解决方案4】:

    我发现这在二传手中使用时风格很差。不可变类通常更适合链接,例如:

    aWithB = myObject.withA(someA).withB(someB);
    

    myObject 属于此类:

    class MyClass {
        withA(TypeA a) {
             this.a.equals(a) ? this : new MyClass(this, a);
        }
    
        private MyClass(MyClass copy, TypeA a) {
            this(copy);
            this.a = a;
        }
    }
    

    构建器模式也很有用,因为它允许最终对象是不可变的,同时防止使用此技术时通常必须创建的中间实例。

    【讨论】:

      【解决方案5】:

      这对建造者来说很有意义,你要做的就是设置一堆东西,创建真实的对象并将建造者扔掉。对于构建器,您不妨去掉方法名称的“set”部分。同样,不可变类型实际上并不需要“get”。

      Something thing = new SomethingBuilder()
          .aProperty(wotsit)
          .anotherProperty(somethingElse)
          .create();
      

      一个有用的技巧(如果你不介意每个类大约 2K 的运行时开销)是使用双括号习惯用法:

      JFrame frame = new JFrame("My frame") {{
          setDefaultCloseOperation(DISPOSE_ON_CLOSE);
          setLocation(frameTopLeft);
          add(createContents());
          pack();
          setVisible(true);
      }};
      

      【讨论】:

      • 这不是“成语”。这是对匿名类的可怕滥用,仅仅因为你给它一个漂亮的名字就不会被接受。
      • 这是一个成语。它使代码不那么混乱。在我使用一种可以更清楚地表达它的语言之前,它对我有用。
      • +1:两个优点。为什么要投反对票?他列出了运行时开销的警告。
      • -1:这种晦涩难懂的技术并不广为人知,在维护中也很难发现。此外,与构建器类相比,它的缺点是在每个调用站点都引入了一个不同的类,而不是为每个人提供一个构建器类。
      • 在不需要时使用内部类会在每次使用时发出额外的文件,并填满 PermGen 内存。我会考虑这种策略的唯一方法是,如果它在一个非常大的类中完成,您可能会从将功能拉出到新的类文件中获得延迟加载的好处。否则,您将支付运行时内存和过多的编译代码大小/类加载时间。
      【解决方案6】:

      这个想法在c++中很常见,它允许操作级联......

      例如

      std::cout

      是流方法

      看到这个:http://www.java2s.com/Tutorial/Cpp/0180__Class/Cascadingmemberfunctioncallswiththethispointer.htm

      【讨论】:

        【解决方案7】:

        我曾经是 Java(以及更糟糕的 C#)在整个对象中创建 getter 和 setter(获取 set 属性)实践的粉丝。这就是我认为的面向对象的用途,但实际上这导致我们只是暴露了对象的核心和实现,而不是真正利用封装。有时您无法摆脱这一点(想到 OR/M),但通常应该设置对象然后执行其功能。我梦想中的对象往往有一个或两个构造函数,并且可能有六个可以工作的函数。

        这样做的原因是,一旦我开始开发 API,就真的需要让事情变得简单。您真的只想添加完成工作所需的复杂性,而 getter 和 setter 虽然本身很简单,但在大量添加时会增加堆的复杂性。当我以不同的顺序加载设置器时会发生什么?有什么不同吗?你确定吗?

        【讨论】:

          【解决方案8】:

          再次@Dan,对于更复杂的情况(考虑到不变性),构建器模式是一个很好的解决方案。

          另外,我主要同意你在吸气剂方面的看法。我相信您所说的是主要遵循“Tell don't ask”范式,我非常同意。但这主要面向吸气剂。

          最后,以上所有内容都适用于具有大量属性的类。如果你只有不到 7 个,我看不出有任何理由。

          【讨论】:

            【解决方案9】:

            在使用 Apache POI excel 库时,我经常这样做;我最终编写了链接的辅助方法,因此我可以应用格式、数据类型、内部单元格数据、公式和单元格定位。

            对于有很多微小的轻量元素需要进行细微的微调的东西,它工作得很好。

            【讨论】:

              【解决方案10】:

              如何使用

              /**
               *
               * @author sanjay
               */
              public class NewClass {
              private int left ;
              private int top;
              public void set(int x,int y)
                  {
                  left=x;
                  top=y;
              }
              public NewClass UP(int x)
                  {
                  top+=x;
                  return this;
              }
              public NewClass DOWN(int x)
                  {
                  top-=x;
                  return this;
              }
              public NewClass RIGHT(int x)
                  {
                  left+=x;
                  return this;
              }
              public NewClass LEFT(int x)
                  {
                  left-=x;
                  return this;
              }
              public void Display()
                  {
                  System.out.println("TOP:"+top);
                  System.out.println("\nLEFT\n:"+left);
              }
              }
              public static void main(String[] args) {
                  // TODO code application logic here
                  NewClass test = new NewClass();
                  test.set(0,0);
                  test.Display();
                  test.UP(20).UP(45).DOWN(12).RIGHT(32).LEFT(20);
                   test.Display();
              

              【讨论】:

              • 这个答案似乎没有解决 OP 的原始问题。
              【解决方案11】:

              我同意@Bernard 的观点,即像这样的方法链接混淆了 setter 的目的。相反,我建议如果您总是创建这样的设置器链,那么您可以为您的类创建一个自定义构造函数,而不是

                  MyClass
                  .setInt(1)
                  .setString("test")
                  .setBoolean(true)
                ;
              

              你会的

              new MyClass(1,"test",true);
              

              这使它更具可读性,如果您愿意,您可以使用它来使您的类不可变。

              【讨论】:

              • 实际上,如果你有大量的 setter 并且你需要调用的 setter 有很多变化,那么这会让它的可读性大大降低。尝试使用重载的构造函数来处理这将是一场噩梦,相信我。
              • 我不同意。构造函数应该只具有创建时所需的参数。为方便起见,构造函数是为了弥补缺乏适当的构造来处理相同的语义。
              • 污染构造函数是个坏主意,除非你有两个或三个参数,tops。将任何最终结果发送给构造函数,并为可变字段使用 setter。
              猜你喜欢
              • 1970-01-01
              • 2022-09-30
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2014-11-17
              • 1970-01-01
              相关资源
              最近更新 更多