我非常好奇为java bean提供不变性的可能性(这里的bean我指的是带有空构造函数的类,为成员提供getter和setter)。显然,这些类不是不可变的,并且它们用于从数据层传输值,这似乎是一个真正的问题。

StackOverflow中提到了一个解决这个问题的方法,叫做<!>“C#<!>”中的不可变对象模式。完全构建后对象被冻结的位置。我有另一种方法,并且非常希望听到人们的意见。

模式涉及两个类Immutable和Mutable,其中Mutable和Immutable都实现了一个提供非变异bean方法的接口。

例如

public interface DateBean {
    public Date getDate();
    public DateBean getImmutableInstance();
    public DateBean getMutableInstance();
}

public class ImmutableDate implements DateBean {
    private Date date;

ImmutableDate(Date date) {
    this.date = new Date(date.getTime());
}

public Date getDate() {
    return new Date(date.getTime());
}

    public DateBean getImmutableInstance() {
        return this;
    }

    public DateBean getMutableInstance() {
        MutableDate dateBean = new MutableDate();
        dateBean.setDate(getDate());
        return dateBean;
    }
}

public class MutableDate implements DateBean {
    private Date date;

public Date getDate() {
    return date;
}

public void setDate(Date date) {
    this.date = date;
}

public DateBean getImmutableInstance() {
    return new ImmutableDate(this.date);
}

    public DateBean getMutableInstance() {
        MutableDate dateBean = new MutableDate();
        dateBean.setDate(getDate());
        return dateBean;
    }
}

这种方法允许使用反射(按照惯例)构造bean,并允许我们在最近的机会转换为不可变的变量。不幸的是,每个bean显然有大量的样板。

我很想听听别人对这个问题的处理方法。 (我抱歉没有提出一个好的问题,可以回答而不是讨论:)

有帮助吗?

解决方案

我想我会使用委托模式 - 使用必须在构造函数中指定的单个DateBean成员创建一个ImmutableDate类:

public class ImmutableDate implements DateBean
{
   private DateBean delegate;

   public ImmutableDate(DateBean d)
   {
      this.delegate = d;
   }

   public Date getDate()
   {
      return delegate.getDate();
   }
}

如果我需要在DateBean d上强制使用不变性,我只需要新的ImmutableDate(d)。我可以很聪明,并确保我没有委派代表,但你明白了。这避免了客户试图将其转变为可变的问题。这很像JDK使用Collections.unmodifiableMap()等(在这些情况下,仍然必须实现变异函数,并编码为抛出运行时异常。如果你有一个没有的基本接口,那就容易多了。存取器)。

然而,它再次是冗长的样板代码,但只需点击几下鼠标就可以为您自动生成像Eclipse这样的好IDE。

如果你最终对很多域对象做了什么,你可能想要考虑使用动态代理甚至是AOP。那么为任何对象构建代理,委派所有get方法,以及根据需要捕获或忽略set方法会相对容易。

其他提示

一些评论(不一定是问题):

  1. Date类本身是可变的,所以你正确地复制它以保护不变性,但我个人更喜欢在构造函数中转换为long并在getter中返回一个新的Date(longValue)。
  2. 你的getWhateverInstance()方法都返回了必须进行强制转换的DateBean,可能是改变界面以返回特定类型的想法。
  3. 说了所有我倾向于只有两个类,一个是可变的,一个是不可变的,如果合适的话,共享一个公共(即get get)接口。如果您认为会有很多来回转换,那么请在两个类中添加一个复制构造函数。
  4. 我更喜欢不可变类将字段声明为 final ,以使编译器也强制执行不变性。
  5. e.g。

    public interface DateBean {
        public Date getDate();
    }
    
    public class ImmutableDate implements DateBean {
        private final long date;
    
        ImmutableDate(long date) {
           this.date = date;
        }
    
        ImmutableDate(Date date) {
           this(date.getTime());
        }
    
        ImmutableDate(DateBean bean) {
           this(bean.getDate());
        }
    
        public Date getDate() {
             return new Date(date);
        }
    }
    
    
    public class MutableDate implements DateBean {
        private long date;
    
        MutableDate() {}
    
        MutableDate(long date) {
           this.date = date;
        }
    
        MutableDate(Date date) {
           this(date.getTime());
        }
    
        MutableDate(DateBean bean) {
           this(bean.getDate());
        }
    
        public Date getDate() {
            return new Date(date);
        }
    
        public void setDate(Date date) {
            this.date = date.getTime();
        }
    
    }
    

我使用接口和转换来控制bean的可变性。我没有看到使用getImmutableInstance()getMutableInstance()等方法使域对象复杂化的充分理由。

为什么不只使用继承和抽象? e.g。

public interface User{

  long getId();

  String getName();

  int getAge();

}

public interface MutableUser extends User{

  void setName(String name);

  void setAge(int age);

}

以下是代码客户端将要执行的操作:

public void validateUser(User user){
  if(user.getName() == null) ...
}

public void updateUserAge(MutableUser user, int age){
  user.setAge(age);
}

它能回答你的问题吗?

YC

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top