Adding functionality to existing classes/interfaces

I just want to check I have this right.

Say I have a new method that would logically be added to Decorator. (For arguments sake, getPropertyNames() that returns a List of Property names exposed by a trait to enable me to build a context-sensitive help menu for building expressions).

My understanding is that due to the existing customized code base, we cannot add new methods to commonly used classes or interfaces that may be subclassed or implemented in custom code. e.g. adding a new method to Decorator or AbstractConfigurable.

The only way to do this and not break custom code is to create a new interface (PropertyNameSource) and implement that in each Decorator that exposes properties.

B.

Thus spake “Brent Easton”:

Adding methods to classes is unlikely to cause any problems. The only way
you could create a problem just by adding a method to a class is if you
added a method with the same name as a method in some custom subclass,
and then only if it had the same type signature.

Adding methods to interfaces will result in custom classes which merely
implement those interfaces (as opposed to inheriting from some core class
which implements such an interaface) failing because they’re missing the
interface’s methods.

Those are the facts, so far as I know.

How we should approach this is a matter of contention. I think that
breakage is necessary sometimes, and that we shouldn’t sacrafice good design
just because it will break custom code. Rodney is much more adamant about
not breaking custom code. I still am not happy about our policy on this
(e.g., this is why we still have no way of purging unused images from
modules). The prospect of adding a new interface every time we need to
add a feature makes me uneasy.

Speculation: I think it might be possible to find a technical solution
to this problem through reflection, though if it’s doable it’s going to be
one hell of a contraption. java.lang.reflect.Proxy lets you create dynamic
proxy clases, where you specify a list of interfaces to implement at runtime.
Possibly we could provide default implementations of the methods you want to
add, and then wrap custom classes which should implement them but don’t in a
Proxy. Blech. That sounds awful, but I’d be willing to put up with something
nasty like this if it could concentrate the nastiness in one place and
give us the freedom to add methods to interfaces.


J.


Messages mailing list
Messages@forums.vassalengine.org
forums.vassalengine.org/mailman/ … engine.org

Post generated using Mail2Forum (mail2forum.com)

Thus spake Joel Uckelman:

I should add here that a strict policy on not adding methods to public
interfaces would have made the tile scheduling problem I fixed over the
weekend insoluble, had we already had a full release containing the
ImageOp interface. (Purging unused images is in the same category—
it’s not logically possible verify that an image isn’t being used without
have a complete report of all images being used…)

More info on this:

interface BrentsNewInterface {
public void frobnicate();
}

class BrentsNewInterfaceHandler() {
private final Method frobnicate =
BrentsNewInterface.class.getMethod(“frobnicate”);

public Object invoke(Object proxy, Method method, Object args)
throws Throwable {
if (method.equals(frobnicate)) {
// here goes the default code for frobnicate()
}
else {
// otherwise this is a method which is not from the interface
// so do something to call that method on the object, something
// like this, I guess:
return method.invoke(proxy, args);
}
}
}

if (!(foo instanceof BrentsNewInterface)) {
// create a proxy which does implement the interface
Foo proxy = (Foo) Proxy.newProxyInstance(
Foo.class.getClassLoader(),
new Class<?> { BrentsNewInterface.class },
new BrentsNewInterfaceHandler()
);

// setup the proxy to be like foo…

// replace foo with Folgers crystals, no one will notice
foo = proxy;
}

Like I said, this is not pretty, but I think it could work. What I don’t
know is whether we want to go this route, nor do I know how slow this
would be. (It means calling every method by reflection when you have
a non-conforming custom class. Blech!) Also, it doesn’t help us in the case
where there is no sane class-independent default implementation of a method
we want to add. (This is the case with image usage.)


J.


Messages mailing list
Messages@forums.vassalengine.org
forums.vassalengine.org/mailman/ … engine.org

Post generated using Mail2Forum (mail2forum.com)

Similarly, adding a new interface to an Abstract class like Decorator or AbstractConfigurable should not be a problem either. You just supply a concrete implementation of the interface. Any old custom code will subclass the new Abstract class and will include the default implmentation of the new interface. This will solve most of my issues.

B.


Messages mailing list
Messages@forums.vassalengine.org
forums.vassalengine.org/mailman/ … engine.org

Post generated using Mail2Forum (mail2forum.com)