views:

112

answers:

6

I have the following hierarchy: Party is a base class, extended by Person and Corporation. I need to change the object class of an entity at some point of its life, and I don't know what's the best way to do this.

I'm modeling the financial world, so I have that a Party can own shares of a Corporation, while only Corporation can have shareholders. Something like this:

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)
}

I build the objects reading data from a source, and it can happen that at first I know only the name of a party, but I don't know whether it is a Person or a Corporation. But I need to create an object, so I create a generic Party. After that, it may happen that I come to know more infos, such that my Party was a Corporation. So, I need to change the class that represents that entity from Party to Corporation. Until now, my solution was to build a new object, copying the old data into it. But I'm not satisfied with it, and I'm wondering what's the best way, pattern or whatever else, to implement what I'd like to achieve.

I thought to the State Pattern, but I think it is best suited for other situations.

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.

A: 

I believe, there is no other way. After you typed 'new' you can no longer go down the class hierarchy. You can only go up.
Upcasting is very logical for, i think, obvious reasons. However, upcasting of object in Java (and in every language i know) is done only by upcasting references, not underlying object. Upcasting of underlying objects is impossible, as far as i know.
As for downcasting of underlying object - it is impossible too. It may be related to general design of language and, which is more important, memory allocation issues. I'll explain.
Suppose class Base can have maximum 4 Mb of RAM, and class Derived - 7 Mb.
So your memory looks like this(1 symbol = 1 Mb): BASEBASEOTHERDERIVED. And suppose you're telling system to convert second base class to Derived. System can do this: BASEDERIVEDERDERIVED. As you can see, object 'OTHER' is corrupted this way. System can also do this: BASEDERIVEDOTHERDERIVED. But in this case it must move addresses of OTHER and DERIVED, which is expensive, maybe even dangerous and hard, especially if there are more than two objects in RAM. So the preffered solution is: BASE____OTHERDERIVEDDERIVED, where _____ is free space, which is similar to freeing memory and allocating new object.

foret
A: 

I have a solution. It's not pretty, but neither is your problem:

Define a new class, Translator. Give the Translator operations that convert a Person to a Corporation, and vice versa. When you need to swap between, put the nasty logic in the Translator. At least all of that logic will be isolated and can be tested.

The reason your problem is so difficult is because you need to know what your building in order to build it. The Builder pattern, in particular, fails because of this reason.

Another option might be to use the Blackboard Architectural pattern. As you learn more about what properties your Party has, observers of the Blackboard can try to determine if its a Person or Corportion.

Alternatively, you could reassess your design entirely and try to come from a new angle.

Mike
the blackboard pattern seems to me related to artificial intelligence, which is not needed in this problem, as I know what the is the "role" of the "entities", but I need to change this role dynamically
cdarwin
A: 

You can't change the class of a java.lang.Object after it has been created. The problem you are trying to solve would probably be trivial to solve with prototype-based programming, as implemented in e.g. JavaScript. However, you are tyring to solve this in Java, which requires some extra work.

I would only keep the Party class, and smarten it up to be able to handle the differences between its potential kinds: Person and Corporation. The following code snippet is by no means a complete solution, just meant to illustrate the approach:

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
}

EDIT: introducing a Property<T> class makes it more type-safe and gets rid of the ugly casts.

Zsolt Török
I already thought to this solution, which is also simpler to persist, but it doesn't seem to me really an object oriented one.
cdarwin
@cdarwin: I agree, this is probably not the smartest way to handle this, but certainly one of the most simple ones. I'm curious what other solutions will come in, always good to learn something new.
Zsolt Török
+2  A: 

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.

This last paragraph got me thinking that a dynamic proxy may address your need. If you have an "entity" E that "is a Person [that] can be represented on other ways than using a class Person". A proxy could wrap your entity E and "implement"/present only the desired interface Person (while hiding any other implemented interfaces or implementation detail about E).

Edit: Since the OP found the answer useful, I'm adding a utility class and demo code:

Some code:

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);
    }
}

Demo:

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);
        }
    }
}

Run:

 *** 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)
Bert F
I'm investigating, but I think your solution is the clearest one and the one that really does what I intended to, even though is quite complex and involves reflection (if I understood well what I read starting from your link)
cdarwin
Yeah, the dynamic proxy is often affiliated with the State pattern, which you already noted was applicable, so no surprise a proxy fits here. It is reflection-based, but the reflection code can be hidden in a utility class like above. I could see a couple of ways to apply a dynamic proxy to this problem, but, to tell the truth, copying the data when the object "evolves" to another class is probably the simples / most understandable / most maintainable approach. Still, it's always fun to play around with this stuff ...
Bert F
A: 

The Decorator pattern is a straightforward technique to accomplish your goal, albeit with at most one decorator wrapping the initial Party instance.

I'm not that familiar with Java's classes, but that Wikipedia link says it's doable.

Huperniketes
A: 

In general, I don't want really to change the class of the objects.

I don't think without changing the class of the object you will be able to do this.Any way i'll just show you an alternative.This is not an answer to your question but it might give you some idea's.

Abstract Party Class:

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.
    }
}

Corp Class:

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();
    }

}

Person Class:

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();
    }

}

Test:

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;
    }

}
Emil
you did what I suggested: to change the type of the object, you create a new object and copy the data from the old obj in the constructor Corp(Party per). In general, when I see instanceof, I think I'm not doing real object oriented programming. This surely works, even though I have not only to copy the data, but also to check the real type of instances...
cdarwin