java - 在实体生命周期的某个时刻改变实体的类型

标签 java design-patterns oop

我有以下层次结构: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 ,但我认为它最适合其他情况。

一般来说,我不希望真的改变对象的类别。我想要的是有一些机制让客户类遵守不同类型的契约。也就是说,我不希望一个类能够在一个人身上调用 setShareholders,我如何实现这一点并不重要,我的意思是也许实体是一个人这一事实可以用其他方式表示,而不是使用人类。

最佳答案

In general, I don't want really to change the class of the objects. What I want is to have some mechanism to make client classes to obey to the contracts of the different types. I.e., I don't want a class to be able to call setShareholders on a Person, and it's not important how I can achieve this, I mean that maybe the fact that an entity is a Person can be represented on other ways than using a class Person.

最后一段让我想到 dynamic proxy可以满足您的需求。如果您有一个“实体”E,“是一个人 [that] 可以用其他方式表示,而不是使用类 Person”。代理可以包装您的实体 E 并仅“实现”/呈现所需的接口(interface) Person(同时隐藏任何其他已实现的接口(interface)或关于 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)

关于java - 在实体生命周期的某个时刻改变实体的类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3906688/

相关文章:

java - 在 Android 的 MVP 设计模式中,我们在哪里放置业务登录?

php - 不允许动态创建公共(public)属性

oop - 我可以在许多从属设备上使用相同的 Jenkins 作业名称(即多态)吗?

python 调用类方法而不初始化类

c# - 时间表 : Retrieve data using SQL, LINQ 或类?

oop - 设计模式查询

java - 完全禁用 java net ssl 调试日志记录

java - Android进度条 只显示进度条,没有背景矩形框

java - 安卓/Java : filenames of contents raw folder to array

java - Azure移动服务: Insert data in to table gives exception