【问题标题】:Best practice to avoid side effects避免副作用的最佳实践
【发布时间】:2013-07-25 17:07:36
【问题描述】:

我有一个关于如何避免对 Java 对象产生副作用的问题。 假设我有一个MyObject 类的实例myObject。我想通过一系列方法/命令来处理 myobject,并且在每个命令级别,我想用方法/命令计算的内容来丰富 myObject

这里 myObject 是一个实例的类:

public class MyObject {
  private int resultOfCommand1;
  private int resultOfCommand2;
  private int resultOfCommand3;
  private int resultOfCommand4;
  ....
  ....
}

以下是必须通过 myObject 处理的方法/命令:

private MyObject command1(MyObject myObject) {
   return myObject.setRresultOfCommand1(1);
}
private MyObject command2(MyObject myObject) {
   return myObject.setRresultOfCommand2(2);
}
private MyObject command3(MyObject myObject) {
   return myObject.setRresultOfCommand3(3);
}
private MyObject command4(MyObject myObject) {
   return myObject.setRresultOfCommand4(4);
}

所以上面显示的设计确实有副作用,我想避免这样的事情。

谁能告诉我避免副作用的最佳方法?总是制作作为参数传递的对象的副本(在本例中为 myObject),对副本进行更改然后返回它会更好吗? 有没有保证多线程安全的最佳方法?

任何帮助将不胜感激。 贺拉斯

【问题讨论】:

  • 你所做的只是看起来过于复杂的getter和setter......然后既然你提到了线程......使用同步
  • 您是否考虑过使其成为流水线模式?通过一组命令类传递对象,每个命令类都修改对象。只有当每个正在处理的命令中都没有肉时,它才会变得过于碎片化。从好的方面来说,要执行的命令可以很容易地改变。看一下这个模式在 netty 中的实现,docs.jboss.org/netty/3.2/api/org/jboss/netty/channel/…

标签: java multithreading jakarta-ee thread-safety


【解决方案1】:

如果你想避免副作用,设置值的方法不能改变实例,而是返回一个新实例(副本)。在该方法中,您使用构造函数来设置所有(最终)值。

例子:

class C {
    private final int x;
    private final int y;

    public C(int _x) {
        super();
        this.x = _x;
        this.y = -1;
    }

    public C(int _x, int _y) {
        super();
        this.x = _x;
        this.y = _y;
    }

    public C setY(int _y) {
        return new C(this.x, _y);
    }
}

为了防止副作用,只需将字段声明为final。如果所有字段都是final,则值(在原始类型的情况下)和对象引用是不可变的。在对象引用的情况下,该对象也必须是不可变的,以实现“完全不变”。

因此,您无需更改副本,而是构建具有新值的副本。

现在可以安全地传递不可变实例了;它不能改变,所以它是线程安全的。

【讨论】:

    猜你喜欢
    • 2019-04-03
    • 2012-02-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-24
    • 2016-11-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多