2

According to Why should I prefer composition over inheritance?, I should prefer composition over inheritance. But what if I need to access the interface and class member in generic way? For example, I have parent class Shape and some child classes:

public class Shape{
    private String customId;
    public String getCustomId(){
        return customId;
    }
    public void setCustomId(String aCustomId){
        customId=aCustomId;
    }

    public abstract void draw();
}

public class Circle extends Shape{
    @Override
    public void draw(){
    }
} 

Some codes to use Shape interface

for(Shape s : shapeArray){
    s.draw();
    //access customId
    if(s.getCustomId().equals(...)){
    }
}

...

public class User{
    private Shape shape;
    public void printInfo(){
        System.out.println("User Shape id:"+shape.getCustomId());
    }
}

But it is violating composition over inheritance, so I change the inheritance to composition+interface:

public interface Shape{
    String getCustomId();
    void setCustomId(String aCustomId);
    void draw();
}

public class Circle implements Shape{
    private String customId;
    public String getCustomId(){
        return customId;
    }
    public void setCustomId(String aCustomId){
        customId=aCustomId;
    }

    public void draw(){
    }
}

I found composition is more unmaintainable in this case because:

  1. when I add a new class, I need to copy and paste

    private String customId;
    public String getCustomId(){
        return customId;
    }
    public void setCustomId(String aCustomId){
        customId=aCustomId;
    }
    
  2. If I need to add a new child class member to Shape, I also need to edit all child class to add the class member as well as getter and setter into it

So my question is, do I need "composition over inheritance" if I need to access both the class member and methods in generic way?

3
  • It’s experience that often composition is better than inheritance. But using composition when inheritance is better - which is not unusual - is just stupid.
    – gnasher729
    Jun 14, 2018 at 6:57
  • Better: »When in doubt choosing inheritance or composition, chances are high that you may be better off using composition than inheritance« Jun 14, 2018 at 7:44
  • What does OP mean with access the interface and class member in a generic way? Jun 18, 2018 at 16:14

6 Answers 6

9

"prefer composition over inheritance" is not a braindead rule saying one should avoid inheritance under all circumstances - that would miss the point of that recommendation, and would be nothing but a form of cargo-cult programming.

What the rule actually means is: using inheritance just for reusage is most often the wrong tool - especially when equivalent reusage can be achieved by composition. Same holds when there are parts inherited which are just needed sometimes, or have to be changed at runtime. However, for polymorphism, using inheritance is fine.

In your example, if each Circle is a Shape with a polymorphic draw method, then using inheritance is a perfectly valid option for this part of the class model. If in your specific context each kind of Shape object always, always, always needs an Id, with no exception, then putting the related implementation into the Shape base class is at least a pragmatic solution, not perfect, but still ok.

The situation changes if one needs sometimes shapes with an Id, and sometimes not, making the Id part of the base class implementation is quite questionable:

  • It restricts the reusability and maintainability of the Shape class, because it violates the single responsibility principle - now the Shape has two different responsibilities: providing a generic draw interface and providing identifyability through an Id.

  • Moreover, the setCustomId makes a Shape mutable, which imposes also certain usage restrictions.

  • And as a minor drawback, each Shape object will allocate a small memory block for an additional string reference, required or not.

So if one wants to create a highly reusable shape class, I would heavily recommend to remove the Id part from the Shape completely, and introduce another class like a TaggedShape, which is composed of an Id and a shape:

 class TaggedShape
 {
     private Shape shape;
     private String customId;
     // ... add some public getters and maybe setters for shape and customId here ...
     public TaggedShape(Shape s, String id)
     {
          shape=s;
          customId=id;
     }

     public Shape getShape(){return shape;}

     public void draw(){shape.draw();}
 }

Now, use the TaggedShape whereever you need shapes with an Id in your program, and a standard Shape whereever you don't require an ID. And whereever you need a TaggedShape as a Shape, use taggedShape.getShape().

As you see, this solution does not force you to implement the same ID code for each new derived class of Shape. It uses composition for the ID part, and inheritance for the draw part. So each new Shape derivation can be combined with the ID, or not, without any necessity to implement some ID interface again. And the Shape class has only "one reason to change" - when the drawing rules change, that's all. If ID part needs to be changed, the Shape class hierarchy is not directly affected any more.

As a final note: in this example, the draw method is purely abstract, so Shape could also be an interface. But all I wrote above stays the same even if there would be some implementation inheritance involved, as long as it belongs to the "drawing responsibility". For example, draw migh not be abstract, but provides an overridable default implementation in Shape. That is still a valid use case for inheritance.

2
  • This particular implementation doesn't seem very extensible. Say, for example, I want to add color to my Shape. But maybe we only sometimes need color, and sometimes we just want the shape info without color. So we decide to use composition. How do you propose implementing this composition? Should we make a Shape, a ColoredShape, a TaggedShape, and a TaggedColoredShape?
    – Phillip
    Apr 12, 2019 at 14:35
  • @Phillip: I would not decide this on the basis of an invented example, but in context of a real world situation. An additional color attribute may belong into each shape, but "nullable". It may belong into each "tagged shape", also nullable. Maybe a ColoredShape is a proxy to a Shape class, both sharing the same IShape interface, and a tagged shape wraps an IShape instead of a Shape. Or one decides to do it the other way round. This is something one should decide when real requirements deliver enough context for a founded design decision.
    – Doc Brown
    Apr 12, 2019 at 14:56
12

Sorry but I don't see any composition here. I just see interface worship.

To favor composition over inheritance you need to do more than blindly slap on an interface. You need to compose and delegate.

public interface Id{
    String getCustomId();
    void setCustomId(String aCustomId);
}

public class IdPlain implements Id { 
    public String getCustomId(){ return customId; }
    public void setCustomId(String aCustomId){ customId=aCustomId; }
    private String customId;
}

public class Circle {
    Circle(Id id){ this.id = id; } // Circle is composed of Id
    public String getCustomId(){ return id.getCustomId(); } // and deligates to Id
    public void setCustomId(String aCustomId){ id.setCustomId(aCustomId); }
    private Id id;

    public void draw(){ // yet Circle can still do it's own thing.
    }
} 

new Circle( new IdPlain() ).draw();

Seems like a lot of work just to avoid inheritance doesn't it? But think about this:

new Circle( new IdFancy() ).draw();

If you say you don't need that kinda flexibility at the cost of all this keyboard typing then fine. Stick with inheritance.


You might have noticed I didn't publish an interface for Circle. That's because I'm not sure how Circle is going to be used yet. The clients that use Circle are the ones that will own the interfaces. Publishing an interface for Circle now, without knowing how it will be used, is premature interface designtm.

I'm arguing for role interfaces here. The fancy principle for that is Interface Segregation. Which means that, no I will not design interfaces for Circle to implement until I see how Circle is actually going to be used. Otherwise I get a speculative explosion of interfacestm: Id, Drawable, ICircle, ReadOnlyId, WriteOnlyId. Bleh, no. I'll design that when I know what all is really needed.

You may argue that you absolutely must finish writing the Circle class now and not make changes to Circle later when you can see how it will be used. Well that sucks but you still can't use that as argument for designing an interface before you know how it will be used. Why? Because of, well, inheritance:

public CircleDrawer inherits Circle implements Drawable {}

(I'm not hand waving here. That works. As is. One line.)

Sorry. There really is no excuse for brain dead interfaces.

5
  • Why would we have Circle offer methods of the same signature as the interface, but not actually implement the interface? I would be thinking of Circle providing access to its Id or else actually implement the Id interface.
    – Erik Eidt
    Jun 13, 2018 at 16:25
  • @ErikEidt better now? Jun 13, 2018 at 16:36
  • yep, that works.
    – Erik Eidt
    Jun 13, 2018 at 17:05
  • Honestly, this solution has still all the drawbacks I listed in my answer (violation of the SRP, mutability etc). This design is IMHO still overly complicated. See my answer for a simpler approach.
    – Doc Brown
    Jun 14, 2018 at 4:57
  • Yeah, I'm still seeing where you're having to completely duplicate the Id interface in Circle without actually implementing the interface. And in addition, all other (sibling) shape classes will still have to duplicate this code. :/
    – Phillip
    Apr 12, 2019 at 14:41
1

Probably, the best approach in your case is to create 2 separate interfaces:

public interface IdAware<T> {
    T getId();
    void setId(T id);
}

public interface Shape {
    void draw();
}

And a combined interface:

public interface ShapeIdAware<T> extends Shape extends IdAware<T> {}

Particular shapes can implement Id, Shape, ShapeIdAware interfaces.

public class Circle implements ShapeIdAware<String> {
    private String id;
    public String getId(){
        return id;
    }
    public void setId(String id){
        this.id=id;
    }

    public void draw(){
    }
}

public class Rectangle implements Shape {

    public void draw(){
    }
}

Using generic types for Id gives the flexibility to use String, Integer or any other type for the Id.

You can provide an abstract basic implementation for the IdAware interface:

public abstract class AbstractIdAware<T> implements IdAware<T> {
     private T id;
     public T getId() {
           return id;
     }
     public setId(T id) {
        this.id = id;
     }
}

Then Circle class will look like:

public class Circle extends AbstractIdAware<String> implements Shape {    
    public void draw() {
    }
}
0

Composition is flexible, implementation inheritance is short. Choice depends on context.

If your base class is internal and will never change, inheritance is fine.

Unfortunately, such class is often just a small utility used in a narrow scope, so benefits of code reuse are miniscule.

0

I agree with @candied_orange that your "composition" solution is missing the composition.

In your example, you have two orthogonal responsibilities. You have a responsibility of drawing, which varies with the different shape type (circle, square, etc.). You have a different responsibility of being identifiable. This does not vary across shape type, but changes for its own reasons. Ideally we aim to separate responsibilities as much as possible. This improves maintainability because a change in a responsibility's requirements does not affect the other responsibilities because they are separate pieces of code. This also improves composability because we can pull together lots of little pieces in many different ways to accomplish the task at hand.

The other thing we need to keep track of are how our dependencies are pointed. We should aim to have less stable things depend on more stable things. This improves maintainability by protecting us from changes unrelated to code.

Keeping this in mind, we can now look at the hierarchy of shapes. We see that the drawing code varies by shape (less stable), but that complexity is nicely hidden behind a simple (little i) interface: the draw method. That interface is very stable both for the service providers (the shape implementations) and for the callers. We also have the attributes that apply to every shape (custom identity in this case). As you noted, this requirement can change from time to time, and so is not very stable. It seems no more stable than the shape implementations and less stable than the draw interface.

Let's first make a code interface (big I) for the draw concept:

public interface Drawable {
    void draw();
}

Now we can implement things that are Drawable:

public class Circle implements Drawable {
    private Point center;
    private int radius;

    public Circle(Point center, int radius) {
        this.center = center;
        this.radius = radius;
    }

    @Override
    public void draw() { /* Draw stuff */ }
}

// etc with more drawable shapes

You'll notice that we did not add any members or methods for custom identity to the shape classes. The responsibility of identity is no more stable than the responsibility of being Drawable, and it is separate from being Drawable. Instead, we need a separate place to encapsulate the identity responsibility and provide a place for composability:

public class Displayable {
    private Drawable shape;
    private String customId;

    public Displayable(Drawable shape, String customId) {
        this.shape = shape;
        this.customId = customId;
    }

    public String getCustomId() {
        return this.customId;
    }

    public void draw() {
        this.shape.draw();
    }
}

We created a new class to contain the identity responsibility. It is composed with a Drawable via its constructor, and it delegates all the drawing tasks to that composed Drawable. We also have the methods for accessing the custom identity of the identifiable shape instance.

Now, should we need more properties for identifiability, we can add them to the Displayable class and they would be available in one place but composable with all the different shapes. Should we need more shapes, they can be easily composed into a Displayable.

1
  • The downside here being that you have to duplicate any of the interface of Drawable that you want clients to be able to use in Displayable.
    – Phillip
    Apr 12, 2019 at 14:47
-1

Maybe you could use composition having an abstract class Shape that implements getCustomId and setCustomId, and have an overridable method draw. Then you will have classes for custom shapes (Circle, Square, etc.) that will inherit from Shape and will have to implement draw. If you don't need to know the specific type of shape, you will use it as a Shape. But, in case you need to know which specific shape the instance is, you need to access it's type, you could use operators like isinstanceof

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.