0

I am creating a multiple wrappers/layers API in Java that go like this

  1. public class Layer1<T extends Layer1>
  2. public class Layer2<T extends Layer2> extends Layer1<Layer2>
  3. public class Layer3 extends Layer2<Layer3>

with methods like public T sees(){ return (T) this;} within each layer

What is the correct way for the above structure to work as to be able to maintain the original object's, we start the chain with, type throughout the call. Aka the following to be feasible: 3rdLayerObject.1stLayerMethod().2ndLayerMethod(); where the 1stLayer method call returns a 1st layer object(instead of subclass 3rd) and therefore calling a 2nd layer method returns an error.

Why the API is structured as such:

1st layer has only the methods that apply to all situations

2nd layer has client specific methods that either override the 1st wrapper's basic implementations or are entirely new

3rd layer has project specific methods that are mainly new methods used only by this project.

Of course, if you think the above structure is flawed and there is another pattern/structure to use to organize this more effectively I am open to any and all ideas.

Leon
  • 197
  • 3
  • 10
  • 1
    Why are you doing this? I *think* you are missing `` from your `extends 1stTestBase<2ndTestBase>`, but this is a horrible class hierachy – Caleth Sep 04 '17 at 14:06
  • @Caleth edited post to add a bit more insight to what I am doing Of course, if you think the above structure is flawed and there is another pattern/structure to use to organize this more effectively I am open to any and all ideas. This is why I posted this in SE stack exchange and not SO to begin with. – Leon Sep 04 '17 at 14:25
  • is `Layer` a euphemism for `Foo`, or is that what you're really calling the class? – Robert Harvey Sep 04 '17 at 14:49
  • @RobertHarvey its a euphemism for each abstraction level as explained at the end of the post just so I can make the question as simple as possible. – Leon Sep 04 '17 at 14:53
  • It looks like ordinary inheritance to me. Whether it is appropriate for your specific situation depends entirely on your needs and requirements. – Robert Harvey Sep 04 '17 at 14:55
  • @RobertHarvey question is why the following isnt feasible: 3rdLayerObject.1stLayerMethod().2ndLayerMethod(); where the 1stLayer method call returns a 1st layer object(instead of subclass 3rd) and therefore calling a 2nd layer method returns an error. – Leon Sep 04 '17 at 14:58
  • 1
    What strikes me as a litte off about your approach is that you are using the term "layer" but you chained call example (i.e. `3rdLayerObject.1stLayerMethod().2ndLayerMethod();`) exposes the layers to the client code, which a layered architecture should try to hide. – Viktor Seifert Sep 05 '17 at 08:45
  • @ViktorSeifert I expose the layers to a client code maintained by me, this is an internal API used for testing. So I will be making a new test object that extends Layer3 and will need to use methods from all layers there. Assuming the structure is correct, the question I have is more on how to correctly use generics to achieve what I want ( 3rd layer object maintaining its type through lower layers method calls) – Leon Sep 05 '17 at 09:07

2 Answers2

2

From this line only 3rdLayerObject.1stLayerMethod().2ndLayerMethod() I suspect that you may want to implement something like this:

class Layer1<T extends Layer1<T>> {
    public T layer1Method(){
        return (T)this;
    }
}

class Layer2<T extends Layer2<T>> extends Layer1<T>{
    public T layer2Method(){
        return (T)this;
    }

}

class Layer3<T extends Layer3<T>> extends Layer2<T>{
    public T layer3Method(){
        return (T)this;
    }

    public static final <U extends Layer3<U>> U newLayer3Instance(){
        return (U)new Layer3();
    }

}

So that you can perform this:

Layer3.newLayer3Instance().layer1Method().layer2Method();

I was struggling for long to get such a solution. Unfortunately in java, generics themselves are not enough. You need to add some magic also..

--- EDIT

Layer 4 would be:

class Layer4<T extends Layer4<T>> extends Layer3<T>{
    public T layer4Method(){
        System.out.println("layer4Method");
        return (T)this;
    }

    public static final <U extends Layer4<U>> U newLayer4Instance(){
        return (U)new Layer4();
    }

}

e.t.c...

Marinos An
  • 136
  • 4
  • Your `getInstance` Method is broken. It can be asked to return an arbitrary subclass of `Layer3`, which it cannot provide - in which case it will fail at runtime. (see [this SO question](https://stackoverflow.com/q/338887/2513200)) – Hulk Sep 05 '17 at 12:14
  • @Hulk I've tested the code on java 1.8 and it works. – Marinos An Sep 05 '17 at 12:27
  • 1
    It works until someone writes a subclass `class Layer4 extends Layer3 {...}` and assigns `Layer4 l4 = Layer3.getInstance();` which compiles perfectly fine but fails with a `ClassCastException`. – Hulk Sep 05 '17 at 12:39
  • @Hulk Correct! A solution could be to rename getInstance to getLayer3Instance (or more correct to newLayer3Instance), and make it final. Every layer that wants to provide a newInstance functionality should create a final newLayerXInstance(). I'll add it on my answer. – Marinos An Sep 05 '17 at 13:58
  • adding `final` to the factory method does not really help here - the method is `static` anyway, and shadowing is not the problem we are facing. `Layer3` would need to be final, and then there is no reason for it to be generic anymore: `final static class Layer3 extends Layer2`. – Hulk Sep 06 '17 at 14:50
  • @Hulk But if it is final and not generic how can we someone create Layer4 if needed? – Marinos An Sep 07 '17 at 09:31
  • You can't - that's the point. – Hulk Sep 07 '17 at 09:52
  • Why you shouldn't be able to extend for as many layers as you want to? – Marinos An Sep 07 '17 at 12:54
  • The problem is your `newLayer3Instance` - method. Dropping it forces your users to write something like `new Layer3<>().layer1Method().layer2Method().layer3Method();`. An alternative implementation might be `public static final Layer3> newLayer3Instance() { return new Layer3<>(); }`, which might be typesafe (have not really thought it through yet). The actual type of such self-referencing types can only be written down with wildcards and diamonds. – Hulk Sep 07 '17 at 13:11
  • I have not understood why `newLayer3Instance()` should be dropped or changed. Why is it a problem that each layer creates an newLayerXInstance like the ones above? Is there a case where a `ClassCastException` will occur? – Marinos An Sep 07 '17 at 13:27
  • Well there is no way to enforce that all classes extending your layers stick to your design - every subclass can break it and cause ClassCastExceptions at runtime. There is no way to enforce that contract at compile time. If you are willing to rely on documentation to communicate that, it might work for your use case. – Hulk Sep 07 '17 at 19:49
0

It does sound like ordinary inheritance. It also sounds like you think you've uncovered an ontological truth about the world in which this software lives. You have a core set of features, followed by a client specific implementation, followed by many projects at a particular client.

Sounds like a reasonable abstraction in light of your ontological commitment. Sounds like a reasonable way to organize your code.

What happens if you need want to share common code from a number of clients or projects within the same client, or even project related code across clients?

Those classes in your hierarchy won't be your units of code reuse anymore. You'll have to start moving the common functionality into other modules. That's where I would start thinking about the architecture, where the abstraction breaks down.

But maybe you never need to do the above.

  • First of thanks for taking the time to reply, on topic, so far I dont need to go there(project related code across clients). What is the main question and the issue I have is that I cant seem to make it work so a chain call of a top level object to a method inherited in a lower level returns a top level object again but "downgrades" to that level object. ie ProjectObject..CoreMethod().ClientSpecificMethod() wont work because the core method will return a core object that obviously wont have access to any client specific or project specific method. – Leon Sep 05 '17 at 06:47