简体   繁体   English

如何防止修改可变对象

[英]How to prevent modification of mutable object

Let's suppose I have a simple class:假设我有一个简单的类:

class Some {
  private Something something;

  public Some() {}

  public Something getSomething() {
    return this.something;
  }

  public void setSomething(Something something) {
    this.something = something;
  }
}

and I have a method:我有一个方法:

void change(Some some) {
  //change state of object via
  some.setSomething(new Something());
}

How can I prevent the state of the Some object from changing inside the change() method?如何防止 Some 对象的状态在change()方法内发生change()

Condition : you can't modify the Some class (ie remove the setSomething() method, make the something field as final , declare the class as final )条件:您不能修改Some类(即删除setSomething()方法,使something字段为final ,将类声明为final

Solution I've found:我发现的解决方案:

interface ISome {
  void setSomething(Something something);
  Something getSomething();
}

class Some implements ISome {
  private Something something;

  public Some() {}

  public Something getSomething() {
    return this.something;
  }

  public void setSomething(Something something) {
    this.something = something;
  }
}

class SomeProxy implements ISome {
  private ISome some;
  
  public SomeProxy(ISome some) {
    this.some = some;
  }

  public Something getSomething() {
    return some.getSomething();
  }

  public void setSomething(Something something) {
    throw new IllegalOperationException("You cant modify an object!");
  }
}

Method:方法:

void change(ISome some) {
  some.setSomething(new Something());
}

And then if we call the change() method like this:然后,如果我们像这样调用change()方法:

change(new SomeProxy());

we will get an exception.我们会得到一个例外。

Is where in java mechanism that could help with that problem and prevent the object from being modified inside the change() method without creating a proxy? java 机制中的哪里可以帮助解决该问题并防止在change()方法中修改对象而不创建代理?

An interface without setter没有 setter 的接口

I agree with the comment of @kaya3, just define an interface without the setter and give that to your function:我同意@kaya3 的评论,只需定义一个没有 setter 的接口并将其提供给您的函数:

interface ISome {
  Something getSomething();
}

class Some implements ISome {
  private Something something;

  public Some() {}

  public Something getSomething() {
    return this.something;
  }

  public void setSomething(Something something) {
    this.something = something;
  }
}


void change(ISome some) {
  //Doesn't compile there no method setSomething in the interface
  some.setSomething(new Something());
}

Please notice that you still modify the Some class, you make it implement an interface and if that class is from a lib and you really can't touch it, it doesn't work.请注意,您仍然修改 Some 类,使其实现一个接口,如果该类来自 lib 而您确实无法触摸它,则它不起作用。 I used it because you did it yourself, but still this isn't a great solution if you really can't modify the class.我使用它是因为你自己做的,但如果你真的不能修改类,这仍然不是一个很好的解决方案。

Still this is great solution to segregate interface and implementation.这仍然是分离接口和实现的好方法。 The interface is part of your public API and nobody can modify the object with the public interface.该接口是公共 API 的一部分,没有人可以使用公共接口修改对象。 So the API always return instances of the interface but use the concrete implementation inside.所以 API 总是返回接口的实例,但使用内部的具体实现。

A proxy that really don't touch the original class一个真正不接触原始类的代理

You can go a step further by not making Some implementing the interface and introduce back your proxy if the class if from an external API:你可以更进一步,不让 Some 实现接口,如果类来自外部 API,则引入你的代理:

class Some {
  private Something something;

  public Some() {}

  public Something getSomething() {
    return this.something;
  }

  public void setSomething(Something something) {
    this.something = something;
  }
}

class SomeProxy implements ISome {
  private Some some;
  
  public SomeProxy(Some some) {
    this.some = some;
  }

  public Something getSomething() {
    return some.getSomething();
  }
}


void change(ISome some) {
  //Doesn't compile there no method setSomething in the interface
  some.setSomething(new Something());
}

This way you never changed the Some class one bit but to work properly you must ensure the the Some class isn't visible from the client code.通过这种方式,您永远不会更改 Some 类,但要正常工作,您必须确保 Some 类在客户端代码中不可见。 You can achieve that thanks to jave 9 modules, thanks to OSGI or thanks to runtime dependency in maven.您可以通过 jave 9 模块、OSGI 或 maven 中的运行时依赖来实现这一点。

The proxy is just a specific case of a more generic and common one代理只是更通用和常见的一种特殊情况

If you think about it, it is specific case of just having a class with a private field for its own use, and publishing part of that object behavior but not all.如果您考虑一下,这是一个特定情况,即只有一个具有私有字段供自己使用的类,并发布该对象行为的一部分而不是全部。 This class doesn't need to do only that.这个类不需要这样做。 She may have a purpose that isn't being a proxy but just happen to have a Some instance to do its job.她的目的可能不是作为代理,而是碰巧有一个 Some 实例来完成它的工作。 She control the Some instance, can call the setter because she is in control, but for the outside world, she only give access to the setter.她控制了 Some 实例,可以调用 setter 因为她在控制中,但是对于外部世界,她只允许访问 setter。

This is the general case.这是一般情况。 That class does implement a concept/feature and encapsulate properly its internal state and implementation.该类确实实现了一个概念/功能并正确封装了其内部状态和实现。 And if you pass that class to any function, nobody can modify the interal state in an unwanted way:如果您将该类传递给任何函数,则没有人可以以不需要的方式修改内部状态:

class OtherBehavior {
  private Some some;
  private OtherThing thing;
  [...]

  public Something getSomething() {
    return some.getSomething();
  }

  public void doStuff() {
    [...]
  }
  [...]
}

void change(OtherBehavior other) {
  // Doesn't compile:
  other.setSomething(new Something()); 
  // Doesn't compile:
  other.some.setSomething(new Something());
  // Ok:
  other.getSomething();
}

Please prefer immutable design in the general case在一般情况下,请选择不可变设计

It is usually recognized that an immutable design is overall better in many cases.通常认为,在许多情况下,不可变设计总体上更好。 So allows the constructor to get the Something() instance, and then make the field final and provide no setter.因此允许构造函数获取Something() 实例,然后将该字段设为final 并且不提供setter。 You'll find that in many case you don't need the setter at all and can just create the immutable object when the Something instance is already available.你会发现在很多情况下你根本不需要 setter 并且可以在 Some 实例已经可用时创建不可变对象。 It is much simpler and robust design.这是更简单和健壮的设计。 If you API need to "change" to a new Something, you could always also create a new Some instance:如果您的 API 需要“更改”为新的东西,您也可以随时创建一个新的 Some 实例:

class Some {
  private final Something something;

  public Some(Something something) {
    this.something = something;
  }

  public Something getSomething() {
    return this.something;
  }
}

This has benefits:这有以下好处:

  • you can add the equals/hashCode and make it participate to maps/set collections safely, you can even compute the hash at construction time and cache it for improved perf (helped us a lot on perf sensitive code)您可以添加 equals/hashCode 并使其安全地参与映射/集合集合,您甚至可以在构建时计算散列并将其缓存以提高性能(在性能敏感代码上对我们有很大帮助)
  • you can share the object arround threads safely您可以安全地共享对象周围的线程
  • you prevent bug occuring by a client calling that setter, so you can pass it anywhere without any risk of bug.您可以防止调用该 setter 的客户端发生错误,因此您可以将其传递到任何地方而没有任何错误风险。
  • you don't need a complex proxy at all, making a copy or deal with exceptions.您根本不需要复杂的代理、复制或处理异常。

But of course this imply you are in control of the Some class.但这当然意味着您可以控制 Some 类。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM