A template for initializing a base class in a constructor of a derived class (or factory)

Imagine that you have a derived class where the base class is something that you cannot change. The base class has many states (many non-constant private members) and many constructors with different number of arguments to initialize a certain subset of the state (the size of the subset depends on the constructor, of course).

Now my derived class is a very light wrapper over the base class. Suppose that it does not add any state of its own and only slightly changes the behavior of a pair of methods (it may make several additional protocols around the call to super.originalMethod() ).

The problem is that I want to take an object of the base class and create a “copy” of it with the same state, but as an instance of my derived class.

It's complicated. I cannot name the "most complete" constructor of the base class, passing all the state from the source, calling getters, because depending on how the base class was created, some of the state values ​​may be rejected by this constructor. For example, you can create a default object with 0-arg ctor, any number of values ​​will be zero. However, it is not legal to pass null values ​​to ctor, which allows you to specify these values.

In addition, the method described above is fragile because if a modification of the base class occurs that adds more state and an “even more complete” constructor (or a state that cannot be set in the constructor, but only through access methods), the copy will no longer be filled .

What I want is similar to `clone (), but rather, it initializes a new object of the same type, initializes the members of the base class of the derived class. I think this does not exist. Any template suggestions that can offer something equivalent?

Keep in mind that I cannot change the base class. If I could, it would be a lot easier.

+4
source share
7 answers

If you can override all public methods, you can save the original object as a delegate

 Class D extends B B src; D(B src){ super(whatever); this.src=src; } public method1(){ src.method1(); } 
+4
source

Enjoy composition over inheritance, perhaps by creating a wrapper class (as shown below). If your base class uses interfaces, your wrapper class can implement the same interfaces and delegate calls to the base class (decorator).

However, there is no reliable strategy that you would describe with inheritance. Even if you used reflection to perform a deep copy, as you indicated, the implementation may change. You have broken encapsulation, and your code will be closely related to the base class.

 public static void main(final String[] args) { final Base base = new Base("Hello"); base.printState(); // Prints "Hello" final Wrapper wrapper = new Wrapper(base); wrapper.printState(); // Prints "Wrapper says Hello" wrapper.clone().printState(); // Prints "Wrapper says Hello" } private static class Wrapper { private final Base base; public Wrapper(final Base base) { this.base = base; } public Wrapper clone() { return new Wrapper(base); } public void printState() { System.out.printf("Wrapper says "); base.printState(); } } private static class Base { private Object state; public Base(final Object state) { if (state == null) { throw new IllegalArgumentException("State cannot be null"); } this.state = state; } public void printState() { System.out.println(state); } } 
+3
source

As others have noted, it’s natural to think about this using delegation and implementing it as a proxy server or decorator. Standard methods for working with these templates require that you have an interface, not a specific class based on it, as well as a dynamic Java proxy.

However, you can accomplish similar things with specific classes using cglib or javassist .

With enough JVM runtime, perhaps using one of the above, or with AspectJ , I think you can even make your existing class implement the new interface.

Hibernate creates proxies for all constant classes, without requiring them to implement an interface, and I believe cglib is used for this.

+1
source

I notice that some people recommend using both composition and inheritance (see below for an example of this anti-pattern).

Please do this only as a last resort . Besides introducing redundant state, your child will expose state and behavior that are completely ignored. This will lead to a very misleading API.

 public static void main(final String[] args) { final Base base = new Base("Hello"); base.printState(); // Prints "Hello" final Wrapper wrapper = new Wrapper(base); wrapper.changeState("Goodbye"); wrapper.printState(); // Prints "Wrapper says Hello" wrapper.clone().printState(); // Prints "Wrapper says Hello". // It seems my state change was completely ignored. What a confusing API... } private static class Wrapper extends Base { private final Base base; public Wrapper(final Base base) { super("Make something up; this state isn't used anyway"); this.base = base; } public Wrapper clone() { return new Wrapper(base); } public void printState() { System.out.printf("Wrapper says "); base.printState(); } } private static class Base { private Object state; public Base(final Object state) { if (state == null) { throw new IllegalArgumentException("State cannot be null"); } this.state = state; } public void changeState(final Object state) { this.state = state; } public void printState() { System.out.println(state); } } 

EDIT : Actually, just don't do it. Ever. This is a terrible, terrible strategy. If you fail to manage all the interaction with the state of the base class (which again makes it very fragile), then very bad will happen. For example, if I change the base class as follows:

 private static class Base { ... // A new method public Object getState() { return state; } ... } 

Oh honey...

 final Wrapper wrapper = new Wrapper(new Base("Foo")); System.out.println(wrapper.getState()); // Prints "Make something up; this state isn't used anyway" 
+1
source

It looks like a job for proxy . (Perhaps if you are google, you may find a better proxy implementation, but the standard enuf in my opinion is good.)

Implemnet a InvocationHandler like this

 class Handler implements InvocationHandler { private Thingie thingie ; public Handler ( Thingie thingie ) { this . thingie = thingie ; } public Object invoke ( Object proxy , Method method , Object [ ] args ) thro ws Throwable { if ( method . getName ( ) . equals ( "target" ) ) { LOG . log ( this ) ; } return method . invoke ( this . thingie , args ) ; } } 
0
source

If the base class does not provide native cloning support, there is no really good way. IMHO, if the correct template is to divide classes into three categories:

-1- Classes that fundamentally cannot be objectively cloned in any way without violating class invariants.

-2- Classes that can be cloned without violating class invariants, but which can be used to obtain other classes that cannot be objectively cloned.

-3- Classes that can be cloned, and which will only be used to obtain classes that can also be cloned.

Classes like -2- or -3- should provide a protected virtual cloning method that will call the parent implementation (if it is one) or Object.Clone (if there is no parent implementation), and then do any cleanup of the class. Classes of type -3- must provide a public cloning method that invokes the virtual method and casts the result to the appropriate type. Inherited classes of type -1, which are derived from types of type -2, should obscure the protected cloning method with something other than a function.

If there is no way to add a secure cloning method to the parent class, there is no way to build a cloned derived class that will not be fragile in terms of implementation details of the parent class. If the parent class is built in accordance with the above pattern, cloning will be implemented purely in derived classes.

0
source

I don’t think you can assign it that way, because of how inheritance works. Say your base class is of type "A". You create your wrapper class of type "B". You can assign instance "B" to enter "A", but you cannot assign instance "A" to enter "B".

-1
source

All Articles