2

More than a few times I've found my self working with a class that is closed (I can't modify it) that I wish implemented a nice narrow interface particular to my needs. My client code is supposed to own the interface but I know of no mechanism to announce that this closed class is a implementation of my narrowed interface.

I'm trying to allow this class to be passed in (dependency injected) to my code (composition) but also anything else that can support the narrowed interface. In other languages duck typing makes this trivial. I'm in java though, so I'm expecting to have to write a whole other class just to wrap the closed class to make this happen. Is there a way I'm missing?

Thanks


EDIT to address dupe:

The Interface Segregation Principle offers no mention of the closed class issue which is the point of this question. Please reconsider marking as dupe of this particular question.


This question: Interface Segregation Principle- Program to an interface, has a good example of the interface segregation principle:

class A {
   method1()
   method2()
   // more methods
   method10()
}
class B {
    A a = new A()

}

will become

interface C {
      method1()
      method2()
}



class A implements C{
      method1()
      method2()
      // more methods
      method10()
  }
  class B {
       C c = new A()

 }   

But note how it requires a change to class A. If A is closed to modification how do I accomplish the same thing cleanly?

Community
  • 1
  • 1
candied_orange
  • 7,036
  • 2
  • 28
  • 62
  • The right way is to ask this on [programmers SE](http://programmers.stackexchange.com/). – Kshitiz Sharma May 21 '14 at 04:26
  • Design questions are no longer welcome here? – candied_orange May 21 '14 at 04:32
  • One route you can sometimes use in this situation is to extend the concrete class and add the interfaces to the subclass. – Mark Peters May 21 '14 at 04:32
  • 2
    @KshitizSharma: I disagree. This is a specific, practical programming question that arises from technical constraints in some, but not all, OO environments. – Mark Peters May 21 '14 at 04:33
  • @Mark Peters that's true, it is a problem that arises from the fact that in java the closed class defines what it's interfaces are, not the clients, that might be written later with an unanticipated need set. – candied_orange May 21 '14 at 04:41
  • 1
    Why on earth was this closed as a duplicate, @JarrodRoberson? Did you even read the question you linked to? The amount of knee-jerk moderation on this site is getting out of hand. – Mark Peters May 21 '14 at 04:46
  • 1
    Sounds like a `Decorator` pattern... but you'll have to create a basic Wrapper around the closed class. – Elliott Frisch May 21 '14 at 04:49
  • @ElliottFrisch To be a decorator it would have to wrap the same interface it implements. The point here is to change what is exposed not to change the behavior of what is exposed. – candied_orange May 21 '14 at 04:54
  • @CandiedOrange Why? With reflection I can access your class internals all I want... why do I want to? – Elliott Frisch May 21 '14 at 04:57
  • 1
    @ElliottFrisch yes you can. And if you do it's on your own head. Don't confuse information hiding with security. All I want to know is if following ISP in this situation is really as ugly as I think it is. – candied_orange May 21 '14 at 05:19
  • @MarkPeters I've had the "Prefer composition over inheritance" principle beat into me but at the moment I really can't see why your extend the closed class idea would be bad. – candied_orange May 21 '14 at 05:40
  • Extending might not be possible if it's marked `final`. Also `protected` members might name clash in the future if the library updates. – weston May 21 '14 at 15:04
  • @Weston if final that certainly shuts the idea of extending down. But many library & system classes aren't. Also, how would protected members name clash if I'm not overriding anything? It's just a trick to stick a new interface on a closed class. – candied_orange May 23 '14 at 03:10

3 Answers3

1

Depending on the situation, one possibility is to wrap all classes in a wrapper class that exposes the said interface, I mean something like this:

public class UnmodifyableObject {
    public void method1();
    public void method2();
    public void method3();
    public void method4();
}

Then you want the interface to look like:

public interface MyInterface {
    public void method1();
    public void method2();
}

As a solution you can wrap your UnmodifyableObject in a WrappedUnmodifyableObject:

public class WrappedUnmodifyableObject implements MyInterface {
    private final UnmodifyableObject unmodifyableObject;

    public WrappedUnmodifyableObject(final UnmodifyableObject unmodifyableObject) {
        this.unmodifyableObject = Objects.requireNonNull(unmodifyableObject, "unmodifyableObject");
    }

    @Override
    public void method1() {
        unmodifyableObject.method1();
    }

    @Override
    public void method2() {
        unmodifyableObject.method2();
    }

    public void method3() {
        unmodifyableObject.method3();
    }

    public void method4() {
        unmodifyableObject.method4();
    }
}

It does nothing more than delegate all methods, and of course it implements the interface.

A few important things to note are that: - You should use composition over inheritance, it might look easier to just extend the class, but you do not control that code, it may remove methods or it may even be final. - This does mean you have to do quite some work. - If you do not want to do the work yourself, you might need to look into tools to change the bytecode either before execution or before loading the class.

Usage of this object would be via:

UnmodifyableObject unmodifyableObject = someExternalMethodCall();
MyInterface myInterfaceObject = new WrappedUnmodifyableObject(unmodifyableObject);
Patrick
  • 1,717
  • 7
  • 21
  • 28
skiwi
  • 66,971
  • 31
  • 131
  • 216
0

Use one or more adapters. Adapters are a type of wrapper that changes the interface of the wrapped class without the need to change its source.

class A {
   method1()
   method2()
   // more methods
   method10()
}

interface SegregatedI1 {
  method1();
}

interface SegregatedI2 {
  method2();
  method3();
}

class ASegregatedI1Adapter implements SegregatedI1 {
    private final A a;

    AI1Adapter(A a){
       this.a = a;
    }

    public void method1(){
      a.method1();
    }
}

Note that A could be an interface or a final class. Also note that an adapter could implement more than one of the segregated interfaces, or you can have separate adapters for each (I'd prefer the latter to keep inline with single responsibility).

weston
  • 54,145
  • 21
  • 145
  • 203
0

Mark Peters suggested simply extending the closed class to avoid the wrapper. Since he hasn't posted an answer based on it I'll write one up.

I normally avoid inheritance in favor of composition as a knee jerk reaction but in this case it really seems to have merit. The main reason being writing a wrapper simply ends up moving the sin of coding to an implementation from the client method into the wrapper. Thus the only thing gained is a level of indirection. If that's all we get and we can find an easier way to get it why not?

Here's some code that slaps a very narrow roleinterface on a very popular and very closed library class: ArrayList.

public class MainClass {

    public static void main(String[] args) {

       OpenArrayList<String> ls = new OpenArrayList<String>();
       ShowClient client = new ShowClient(ls);

       ls.add("test1");       
       client.show();

       ls.add("test2");       
       client.show();
    }
}

//Open version of ArrayList that can implement roleinterfaces for our clients
public class OpenArrayList<E> extends ArrayList<E> implements ShowState {
    private static final long serialVersionUID = 1L;
}

//Roleinterface for ShowClient
public interface ShowState {
    public int size();
    public String toString(); 
}

//Show method programmed to a roleinterface narrowed specifically for it
public class ShowClient {

    private ShowState ss;

    ShowClient(ShowState ss) {
        this.ss = ss;
    }

    void show() {
        System.out.println( ss.size() );
        System.out.println( ss.toString() );
    }
}

So, if you're going to do Interface Segregation when using a class closed to modification is there a reason not to do it this way?

candied_orange
  • 7,036
  • 2
  • 28
  • 62