candied_orange
candied_orange

Reputation: 7344

I want to follow the interface segregation principle but the class is closed. Is a wrapper the right way?

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?

Upvotes: 2

Views: 1315

Answers (3)

skiwi
skiwi

Reputation: 69379

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

Upvotes: 1

candied_orange
candied_orange

Reputation: 7344

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?

Upvotes: 0

weston
weston

Reputation: 54801

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

Upvotes: 0

Related Questions