我有以下层次结构:Party是基类,由Person和Corporation扩展。 我需要在生命的某个阶段更改实体的对象类,我不知道最好的方法是什么。
我正在模拟金融世界,所以我认为一个党可以拥有公司的股份,而只有公司可以拥有股东。像这样:
class Party {
public Set getShares();
}
class Corporation extends Party {
public Set getShareholders();
public void setShareholders(Party ss);
}
class Person extends Party {
... (some method still unknown)
}
我构建从源读取数据的对象,并且最初我只知道一方的名称,但我不知道它是个人还是公司。 但我需要创建一个对象,所以我创建了一个通用的Party。在那之后,我可能会发现更多的信息,这样我的党就是公司。所以,我需要将代表该实体的类从Party更改为Corporation。 到目前为止,我的解决方案是构建一个新对象,将旧数据复制到其中。 但我对它并不满意,我想知道实现我想要实现的目标是什么是最好的方法,模式或其他什么。
我想到State Pattern,但我认为它最适合其他情况。
一般来说,我不希望真的更改对象的类。我想要的是有一些机制让客户端类服从不同类型的契约。也就是说,我不希望一个类能够在一个Person上调用setShareholders,并且我如何实现这一点并不重要,我的意思是,实体是一个Person的事实可以通过其他方式来表示,而不是使用班级人员。
答案 0 :(得分:2)
一般来说,我不想真正改变对象的类。我想要的是有一些机制让客户端类服从不同类型的契约。也就是说,我不希望一个类能够在一个Person上调用setShareholders,并且我如何实现这一点并不重要,我的意思是,实体是一个Person的事实可以通过其他方式来表示,而不是使用班级人员。
最后一段让我想到dynamic proxy可能会满足您的需求。如果你有一个“实体”E,那么“是一个可以用其他方式表示的人,而不是使用类人”。代理可以包装您的实体E并“实现”/仅显示所需的接口Person
(同时隐藏任何其他实现的接口或关于E的实现细节)。
编辑:由于OP发现答案很有用,我正在添加一个实用程序类和演示代码:
一些代码:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* Utilities to support using dynamic proxies
*/
public class DynamicProxy {
/**
* An invocation handler that passes calls to its delegate. This class
* could be subclassed to provide dynamic method invocation handling
* while still being able to fall back to the delegate object's methods.
*
* @see InvocationHandler
*/
public static class DelegatingInvocationHandler
implements InvocationHandler {
/** The object this proxy is wrapping */
private final Object delegate;
/**
* Creates a delegate invocation handler around an object
*
* @param object
* The object to wrap
*/
public DelegatingInvocationHandler(final Object delegate) {
this.delegate = delegate;
}
/* (non-Javadoc)
*
* @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object,
* java.lang.reflect.Method, java.lang.Object[])
*/
@Override
public Object invoke(final Object proxy, final Method m,
final Object[] args) throws Throwable {
Object result;
try {
result = m.invoke(delegate, args);
} catch (final InvocationTargetException e) {
throw e.getTargetException();
} catch (final Exception e) {
throw new RuntimeException("unexpected invocation exception: "
+ e.getMessage());
}
return result;
}
}
/**
* Create a dynamic proxy that implements only a specified subset of the
* original object's implemented interfaces. The proxy allows you to
* essentially hide the other interfaces implemented by the original
* object.
*
* @param delegate
* the object that the proxy "proxies" for (a.k.a. the delegate
* that handles the calls the proxy "allows" through)
* @param requiredInterface
* one of the interfaces of the delegate that the proxy exposes
* @param moreInterfaces
* any additional interfaces of the delegate to expose
* @return the proxy
* a proxy for the delegate that can be cast to any of the
* specified interfaces
*/
public static <T> T createSelectiveProxy(final T delegate,
final Class<T> requiredInterface,
final Class<?>... moreInterfaces) {
if (delegate == null) {
throw new IllegalArgumentException(
"The delegate object cannot be null");
}
return createProxy(new DelegatingInvocationHandler(delegate),
requiredInterface, moreInterfaces);
}
/**
* Creates a proxy using the specified invocation handler.
*
* @param object
* the implementing object that proxy wraps
* @param invocationHandler
* the interfaces
* @param moreInterfaces
* the interfaces
* @return the object
*/
@SuppressWarnings("unchecked")
public static <T> T createProxy(final InvocationHandler invocationHandler,
final Class<T> requiredInterface,
final Class<?>... moreInterfaces) {
if (invocationHandler == null) {
throw new IllegalArgumentException(
"The invocation handler cannot be null");
}
final int size = (moreInterfaces != null ? moreInterfaces.length : 0);
final Class<?>[] interfaces = new Class<?>[size + 1];
interfaces[0] = requiredInterface;
System.arraycopy(moreInterfaces, 0, interfaces, 1, moreInterfaces.length);
return (T) Proxy.newProxyInstance(invocationHandler.getClass()
.getClassLoader(), interfaces, invocationHandler);
}
}
演示:
public class DynamicProxyDemo {
private interface A {
void methodA();
}
private interface B {
void methodB();
}
private static class Foo implements A, B {
public void methodA() {
System.out.println("A");
}
public void methodB() {
System.out.println("B");
}
}
private DynamicProxyDemo() {}
public static void main(final String[] args) {
final Foo foo = new Foo(); // implements both interfaces
// calls foo's methods, but only A methods
final A a = DynamicProxy.createSelectiveProxy(foo, A.class);
// calls foo's methods, but only B methods
final B b = DynamicProxy.createSelectiveProxy(foo, B.class);
// calls foo's methods, but A and B methods
final A ab = DynamicProxy.createSelectiveProxy(foo, A.class, B.class);
System.out.println("\n *** Call a method A.methodA() on proxy 'a'");
a.methodA();
try {
System.out.println("\n *** Call a method B.methodB() on proxy 'a' (throws exception)");
((B) a).methodB();
} catch (final Exception ex) {
ex.printStackTrace(System.out);
}
System.out.println("\n *** Call a method B.methodB() on proxy 'b'");
b.methodB();
try {
System.out.println("\n *** Call a method A.methodA() on proxy 'b' (throws exception)");
((A) b).methodA();
} catch (final Exception ex) {
ex.printStackTrace(System.out);
}
System.out.println("\n *** Call a method A.methodA() on proxy 'ab'");
ab.methodA();
System.out.println("\n *** Call a method B.methodB() on proxy 'ab'");
((B) ab).methodB();
// ClassCastException: $Proxy0 cannot be cast to DynamicProxy$Foo
try {
System.out.println("\n *** Call a method 'A' of class 'Foo' on proxy 'a' (throws exception)");
((Foo) a).methodA();
} catch (final Exception ex) {
ex.printStackTrace(System.out);
}
// ClassCastException: $Proxy1 cannot be cast to DynamicProxy$Foo
try {
System.out.println("\n *** Call a method 'B' of class 'Foo' on proxy 'b' (throws exception)");
((Foo) b).methodB();
} catch (final Exception ex) {
ex.printStackTrace(System.out);
}
// ClassCastException: $Proxy0 cannot be cast to DynamicProxy$B
try {
System.out.println("\n *** Call a method B.methodB() on proxy 'a' (throws exception)");
((B) a).methodB();
} catch (final Exception ex) {
ex.printStackTrace(System.out);
}
// ClassCastException: $DynamicProxy1 cannot be cast to DynamicProxy$A
try {
System.out.println("\n *** Call a method A.methodA() on proxy 'b' (throws exception)");
((A) b).methodA();
} catch (final Exception ex) {
ex.printStackTrace(System.out);
}
}
}
执行命令
*** Call a method A.methodA() on proxy 'a'
A
*** Call a method B.methodB() on proxy 'a' (throws exception)
java.lang.ClassCastException: net.bertfernandez.reflection.$Proxy0 cannot be cast to net.bertfernandez.reflection.DynamicProxyDemo$B
at net.bertfernandez.reflection.DynamicProxyDemo.main(DynamicProxyDemo.java:49)
*** Call a method B.methodB() on proxy 'b'
B
*** Call a method A.methodA() on proxy 'b' (throws exception)
java.lang.ClassCastException: net.bertfernandez.reflection.$Proxy1 cannot be cast to net.bertfernandez.reflection.DynamicProxyDemo$A
at net.bertfernandez.reflection.DynamicProxyDemo.main(DynamicProxyDemo.java:59)
*** Call a method A.methodA() on proxy 'ab'
A
*** Call a method B.methodB() on proxy 'ab'
B
*** Call a method 'A' of class 'Foo' on proxy 'a' (throws exception)
java.lang.ClassCastException: net.bertfernandez.reflection.$Proxy0 cannot be cast to net.bertfernandez.reflection.DynamicProxyDemo$Foo
at net.bertfernandez.reflection.DynamicProxyDemo.main(DynamicProxyDemo.java:73)
*** Call a method 'B' of class 'Foo' on proxy 'b' (throws exception)
java.lang.ClassCastException: net.bertfernandez.reflection.$Proxy1 cannot be cast to net.bertfernandez.reflection.DynamicProxyDemo$Foo
at net.bertfernandez.reflection.DynamicProxyDemo.main(DynamicProxyDemo.java:81)
*** Call a method B.methodB() on proxy 'a' (throws exception)
java.lang.ClassCastException: net.bertfernandez.reflection.$Proxy0 cannot be cast to net.bertfernandez.reflection.DynamicProxyDemo$B
at net.bertfernandez.reflection.DynamicProxyDemo.main(DynamicProxyDemo.java:89)
*** Call a method A.methodA() on proxy 'b' (throws exception)
java.lang.ClassCastException: net.bertfernandez.reflection.$Proxy1 cannot be cast to net.bertfernandez.reflection.DynamicProxyDemo$A
at net.bertfernandez.reflection.DynamicProxyDemo.main(DynamicProxyDemo.java:97)
答案 1 :(得分:0)
我相信,没有别的办法。键入“new”后,您将无法再进入类层次结构。你只能上去。
我认为,上行是非常合乎逻辑的,原因很明显。但是,在Java(以及我所知道的每种语言)中对象的向上转换只能通过向上转换引用而不是基础对象来完成。据我所知,基础物体的上行是不可能的
至于潜在客体的预测 - 它也是不可能的。它可能与语言的一般设计有关,更重要的是内存分配问题。我会解释一下。
假设类Base
可以有最大4 Mb的RAM,而类Derived
- 7 Mb。
所以你的记忆看起来像这样(1符号= 1 Mb):BASEBASEOTHERDERIVED
。假设你告诉系统将第二个基类转换为Derived。系统可以执行此操作:BASEDERIVEDERDERIVED
。如您所见,对象'OTHER'以这种方式被破坏。系统也可以这样做:BASEDERIVEDOTHERDERIVED
。但在这种情况下,它必须移动OTHER和DERIVED的地址,这是昂贵的,甚至可能是危险和困难的,特别是如果RAM中有两个以上的对象。所以优先解决方案是:BASE____OTHERDERIVEDDERIVED
,其中_____
是可用空间,类似于释放内存和分配新对象。
答案 2 :(得分:0)
我有一个解决方案。它不漂亮,但你的问题也不是:
定义一个新类,Translator。为转换程序提供将人员转换为公司的操作,反之亦然。当你需要交换时,把讨厌的逻辑放在翻译器中。至少所有逻辑都是隔离的,可以进行测试。
您的问题如此困难的原因是因为您需要知道 您的建筑物才能构建它。特别是Builder模式由于这个原因而失败。
另一种选择可能是使用Blackboard Architectural pattern。当您了解更多关于您的派对有哪些属性时,Blackboard的观察者可以尝试确定它是人还是Corportion。
或者,您可以完全重新评估您的设计并尝试从一个新角度出发。
答案 3 :(得分:0)
创建后,您无法更改java.lang.Object
的类。您尝试解决的问题可能无法通过prototype-based programming来解决,例如在{ JavaScript的。但是,你需要在Java中解决这个问题,这需要一些额外的工作。
我只会保留党的阶级,并使其聪明起来,以便能够处理其潜在种类之间的差异:人和公司。以下代码片段绝不是一个完整的解决方案,只是为了说明这种方法:
class Party {
PartyKind kind;
Map<String, Property<?>> properties;
public Property getProperty(String name) {
return properties.get(name);
}
}
class Property<T> {
T value;
}
enum PartyKind {
PERSON,
CORPORATION
}
编辑:引入Property<T>
类使其更加类型安全并摆脱丑陋的演员阵容。
答案 4 :(得分:0)
Decorator pattern是一种直接的技术来实现你的目标,尽管最多只有一个装饰器包装了最初的Party实例。
我不熟悉Java的类,但维基百科链接说它是可行的。
答案 5 :(得分:0)
一般来说,我不想真正改变对象的类。
我不认为如果不改变对象的类,你就可以做到这一点。任何方式我只会告诉你一个替代方案。这不是你问题的答案,但它可能会给你一些想法。
抽象派对类:
abstract class Party {
protected String nameOfParty;
private Set<String> shares=new HashSet<String>();
public Party(String name) {
nameOfParty=name;
}
public Set<String> getShares(){
return shares;
}
public void addShare(Party corp) {
shares.add(((Corp)corp).nameOfParty);//only add corporation's share.
}
}
公司类:
public class Corp extends Party {
private Set<String> shareHolders = new HashSet<String>();
public Corp(String name) {
super(name);
}
public Corp(Party per) {
super(per.nameOfParty);
}
public Set<String> getShareholders() {
return shareHolders;
}
public void addShareholder(Party shareHolder) {
this.shareHolders.add(shareHolder.nameOfParty);
}
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("Name Of Corp : ").append(nameOfParty);
sb.append("\nShare's: ").append(this.getShares());
sb.append("\nShare Holder's : ").append(shareHolders);
return sb.toString();
}
}
人员类:
public class Person extends Party {
public Person(String name) {
super(name);
}
public Person(Party name) {
super(name.nameOfParty);
}
public void unknowMethod() {
}
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("Name Of Person : ").append(nameOfParty);
sb.append("\nShare's: ").append(this.getShares());
return sb.toString();
}
}
<强>测试强>
public class Test {
public static void main(String[] args) {
/*
* Initially consider all party's to be person's
* or create party with anonymous class if party information
* is not available at that time of creation.
*/
Party google = new Party("Google") {}; //Party with anonymous class
Party yahoo = new Person("Yahoo");
Party george = new Person("George");
Party ali = new Person("Ali");
Set<Party> list = new HashSet<Party>();
list.add(google);
list.add(yahoo);
list.add(george);
list.add(ali);
// later came to know that google and yahoo are corporation's
google = changeToCorp(list, google);
yahoo = changeToCorp(list, yahoo);
for (Party pty : list) {
if (pty instanceof Person) {
Person p = (Person) pty;
p.unknowMethod();// access method's of person
p.addShare(yahoo);
p.addShare(google);
/*p.addShare(ali);
will throw exception since ali is a person and cannot be added as share*/
System.out.println("\n" + pty);
}
if (pty instanceof Corp) {
Corp c = (Corp) pty;
c.addShareholder(george);// access method's of corp
c.addShareholder(yahoo);
c.addShare(google);
c.addShareholder(ali);// share holder's can be either a person or a corporation
System.out.println("\n" + pty);
}
}
}
static Corp changeToCorp(Set<Party> se, Party per) {
se.remove(per);
Corp p = new Corp(per);
se.add(p);
return p;
}
}