由Java工厂生产的只读对象

  • 本文关键字:只读 对象 Java 工厂 java
  • 更新时间 :
  • 英文 :


在以前的C++代码中,我在创建一个可以输出"只读"对象的工厂时使用了友元类,这意味着由于对象在整个代码中都被使用,因此不会有无意中更改/损坏它们的风险。

在Java中是否有类似的方法来实现这一点,或者我是否过于防御?

使用final关键字。这个关键字可以将类/方法标记为不可扩展,并将字段/变量标记为不可变。

您将使用private构造函数隐藏对象的默认构造函数,并强制参数化构造函数初始化所有必要的最终字段。

你唯一的问题是工厂有点多余。由于对象的所有字段都是最终字段,因此必须在对象构建时使用所有工厂方法。

示例:

public final class DataObject
{
protected final String name;
protected final String payload;
private DataObject()
{
}
public DataObject(final String name, final String payload)
{
this.name = name;
this.payload = payload;
}
}
// Using the factory
DataObject factory = new Factory().setName("Name").setPayload("Payload").build();
// As opposed to
DataObject dao = new DataObject("Name", "Payload");
// ==> Factory becomes redundant, only adding extra code


不含final:的解决方案

恐怕你将不得不忘记C++的不变性机制。如果您有巨大的数据对象(即有很多setter),那么工厂模式从来都不是一个糟糕的选择,但您无法真正避免构建对象的可变性。您可以做的是使数据对象成为工厂的内部类,并使setter成为私有的。这样,只有工厂才能访问设置器。这将是最适合您的方法(即模拟不变性)。

示例:

public class Factory 
{
private String name;
private String payload;
public Factory setName(final String name)
{
this.name = name;
}
public Factory setPayload(final String payload)
{
this.payload = payload;
}
public DataObject build()
{
DataObject newObj = new DataObject();
newObj.setName( this.name );
newObj.setPayload( this.payload );
return newObj;
}
public class DataObject
{
// fields and setters, ALL PRIVATE
}
}

您可以将对象类和工厂放在同一个包中,并使可变方法包具有作用域(这是Java中的默认可见性,只是不要将方法声明为publicprivateprotected),也可以使类真正不可变并在构造函数中完成所有工作。如果您发现构造函数中的参数太多,并且很难理解,请考虑生成器模式。

在Java中没有与友元类直接相等的类。但是,看看http://docs.oracle.com/javase/tutorial/java/javaOO/accesscontrol.html.

如果您的对象实现了一个接口,并且工厂返回接口类型而不是具体类型(更好),那么您可以使用java.lang.reflect.Proxy在运行时创建动态代理,以拦截对目标对象的所有方法调用。在下面的代码示例中,FooFactory类创建一个Foo实例(每次调用其createFoo方法时),但不直接返回实例,而是返回一个动态代理,该代理实现与Foo相同的接口,并且动态代理拦截并委托所有方法调用到Foo实例。当您没有类代码时,这种机制有助于控制对类的访问。

public class FooFactory {

public static IF createFoo() {
//Create Foo instance
Foo target = new Foo(); // Implements interface IF
//Create a dynamic proxy that intercepts method calls to the Foo instance
IF fooProxy = (IF) Proxy.newProxyInstance(IF.class.getClassLoader(),
new Class[] { IF.class }, new IFInvocationHandler(target));
return fooProxy;
}
}
class IFInvocationHandler implements InvocationHandler {
private Foo foo;
IFInvocationHandler(Foo foo) {
this.foo = foo;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
if (method.getName().equals("setMethod")) {
// Block call
throw new IllegalAccessException();
} else {
// Allow call
method.invoke(proxy, args);
}
return null;
}
}
class Foo implements IF {
public void setMethod() {
} // method that is not allowed to call
public void getMethod() {
}
}
interface IF {
void setMethod(); // method that is not allowed to call
void getMethod(); // method that is allowed to call
}

在Java中,最接近C++朋友类的是包私有访问。

SomeObject.java:

package somewhere.someobjandfriends;
public class SomeObject {
Object aField;   // field and constructor
SomeObject() {}  // are package-only access
public void aMethod() {
System.out.println(this);
}
}

SomeObjFactory.java:

package somewhere.someobjandfriends;
public class SomeObjFactory {
public SomeObject newHelloWorld() {
return new SomeObject() {
{
aField = "hello world!";
}
@Override
public String toString() {
return aField.toString();
}
};
}
}

包之外的任何地方都可以看到SomeObjectaMethod,但只能通过工厂创建新实例。

最新更新