3

A coworker and I are having a debate. In our app, there are components that behave slightly differently based on how we want it to be presented. Thus far, we have agreed our inheritance should look akin to:

     Component
     /      \
  Fixed    Resizeable

wherein Fixed would have a size property and Resizeable would have properties for bounds and initial size. However, there are ~20 different components that fit into 4 variants: large, medium, medium-large (resizeable), and medium-small (resizeable).

We disagree with how the components should be set into these variants.

His solution:

// make a class that sets the properties of the variant
class MediumLargeResizeableComponent extends ResizeableComponent {
    MediumLargeResizeableComponent() {
        super(MEDIUM, LARGE); // some constants for the bounds
    }
}

class CustomComponent extends MediumLargeResizeableComponent {
    CustomComponent() {
        // doesn't have to set the size properties
    }
}

This solution uses a different class for each variant that sets the properties. I see this as a problem because you need a new class for each variant and doesn't seem like a good use of inheritance. He thinks it makes it easier to understand what the class is doing. He also says it makes it easier to change all the classes that use that variant.

My solution:

// inherit directly and set the properties 
class CustomComponent extends ResizeableComponent {
    CustomComponent() {
        super(MEDIUM, LARGE); // constants for the bounds
    }
}

This solution is simpler and leaves the customization to the component.

The only technical reason I can see against his solution is that the technique doesn't scale to other properties without creating a lot of variants. He doesn't see this as a problem because we don't have a need for other properties like this.

Is his solution - using inheritance to set properties - a bad model? Or is it acceptable in situations like this?

Edit:

I feel like there may be some confusion. The 20 different classes are simply different components that each do different things that are independent, but each of those classes only fit 1 of the size variants. As above, CustomComponent is resizeable from medium to large and nothing else.

  • I know this is a small, almost petty example but its approximately what the debate was about. – kmdreko Nov 29 '16 at 3:42
  • Size is only one way of categorizing components. Do you foresee any other future needs that will require you to categorize in a different way, which would have forced you to dismantle your size-based inheritance hierarchy? For example, animals can be categorized by size, as well as biological class / order (mammals, reptiles, etc). The inheritance hierarchy in programming languages can only support one categorization, unless you apply "composition over inheritance" to separate the size aspects into one hierarchy and the biological class / order into a different hierarchy. – rwong Nov 29 '16 at 5:39
  • right now the sizing of the components is the only thing that can be generalized across them. I could conjure up other ideas but none really apply to our scenario. – kmdreko Nov 29 '16 at 5:46
  • 1
    Why isn't size simply something passed into the disparate components? If they can't be generalized what methods are ever going to be in Component? There should be a common way to use any component or this hierarchy is pointless. I thought they were all going to be "presented". – candied_orange Nov 29 '16 at 5:50
  • sorry if I misspoke, but what I mean't was there isn't another way to categorize these components beyond how they are displayed. @CandiedOrange the common base between the components is that they all are able to react to the user's input and display information (I know its vague, but they do do something) – kmdreko Nov 29 '16 at 5:56
8

I've come to love polymorphism but inheritance makes me itch. Inheritance chains give me hives. Why? They lead to the yo yo problem. Oh sure, you think you're chain is small and cute now, but they grow up, chew up the furniture -er- man hours, and pretty soon I'm the one that ends up taking care of them.

So yeah I'd prefer yours over his but, well...

There may be other reasons to do what you're doing but what you've shown me so far makes me want to create:

large = new BoundedComponent(LARGE, LARGE);
medium = new BoundedComponent(MEDIUM, MEDIUM);
mediumLarge = new BoundedComponent(MEDIUM, LARGE);
mediumSmall = new BoundedComponent(SMALL, MEDIUM);

Why? Well for one it drives me batty to see a parameter become part of a class name. Please tell me your 20 different components aren't going to be variations on SmallLargeResizeable. God help us if we have to add tiny or extra large.

In short you haven't sold me on the idea of Fixed at all. Polymorphism is a wonderful thing but I don't think it was meant for this. I normally reach for composition before inheritance but I don't see the need for that either. Simple bounds seem to solve this problem.

-

Fixed would have a size property

This right here is suspicious. Only Fixed gets a size property? So now I have to KNOW that I'm talking to a Fixed? I can't just treat it like a Component?

and Resizeable would have properties for bounds and initial size

And again I have to KNOW what I'm talking to. Bounds and initial size only live on a Resizeable so I'd better not call them on a Fixed. I don't want to know about the components problems! Just display already you silly component.

To justify these size components what I want are methods that I can call that do WHATEVER they do without having do know what they'll do. Those things can be different because of what they really are but when I use them I don't want to have to think about it.

someComponent.display() should be something I can do to any component. If that component was a new BoundedComponent(LARGE, LARGE) or a new TextBoxComponent(new Resizeable(MEDIUM, LARGE)) is none of my business.

Once I call it though it's very nice when what is displayed changes based on what was actually hiding under the covers. If you have different behavior you'd like to express by all means create some polymorphic classes to express it. But it should be more than just size enums.

  • The 20 different components all have code to do what they need to so they can't be generalized as you have (but fit one of the 4 types). A Fixed component could theoretically be resizeable with both bounds the same value but its not relevant to the question. I'm asking if there's a more solid reason behind hot having the variant classes beyond "it drives me batty" – kmdreko Nov 29 '16 at 5:27
  • 4
    @vu1p3n0x You missed the solid reason: What are you going to do when you're requirements change and suddenly there are new sizes? The proper use of polymorphism is to allow changes in behavior while usage is identical. Hard coding parameters don't show that. Show me changes in logic. Components that know how to display for the web. the console, to print. If that's the kind of thing your 20 do then sure, you need polymorphism. Still don't see the need for inheritance. It has uses but I still don't see it here. – candied_orange Nov 29 '16 at 5:44
5

Eventually you will no doubt discover that there are a number of features that components can have, and each component has zero or more of these features. Resizable and fixed size are just two of the many features you will likely end up with. This is a classic problem that all component framework designers face.

If your programming language does not support multiple inheritance then there is no inheritance structure that works. The problem is that there are too many permutations of features and they don't naturally fit an inheritance hierarchy. In fact I would go further by saying that there are very few problems where inheritance is a good fit, and I have been strongly discouraging the use of inheritance for many years with no regrets at all.

The solution that fits your problem is mixins. I don't think you mentioned which language you write in, but there are standard patterns for implementing mixins for most languages.

In short, you're both wrong. You should define interfaces like IFixedSize IResizable IContainer IDrawable etc, then you can make components that implement any combination of these interfaces.

The problem with inheritance is that it creates structure, and with that structure comes rigidity. What you actually want in your software is flexibility, so inheritance is always bad.

The reason that programmers use inheritance is to reuse the functionality in the base class. They accept rigidity as the cost of code reuse, but they overlooked the fact that there are other ways to reuse code that does not have this cost.

Consider the following example:

public interface ISize
{
    float Width { get; set; }
    float Height { get; set; }
}

public interface ISizeFactory
{
    ISize Create();
}

public class MyComponent : ISize
{
    private ISize _size;

    public MyComponent(ISizeFactory sizeFactory)
    {
        _size = sizeFactory.Create();
    }

    public float Width
    {
        get { return _size.Width; }
        set { _size.Width = value; }
    }

    public float Height
    {
        get { return _size.Height; }
        set { _size.Height = value; }
    }
}

In this example the MyComponent class "inherits" the ability to change size from a reusable implementation without sacrificing any flexibility. In this example any component can have sizeability added to it without any requirement for it to inherit from a specific base class that might come with a whole load of unwanted baggage.

This has total flexibility and full code reuse - all the benefits of inheritance and none of the cost.

  • My question already acknowledges that having multiple differing "features" using a setup like this would be a problem. However, the question I'm asking is whether in this situation with one "features" being set through inheritance is a problem. – kmdreko Nov 29 '16 at 7:10
  • 2
    Over many years of working as a developer, most of the really painful refactoring exercises I have had to deal with were made painful mostly because of inheritance. I stopped using inheritance about 10 years ago, and have never looked back, it's one of the best decisions I have made. Other people are a little skeptical to begin with, but after a little while always become converted. So yes, almost all uses of inheritance are a bad idea. – bikeman868 Nov 29 '16 at 8:06
  • 2
    @vu1p3n0x: Can you guarantee that there will never be a second similar classification property for the entire lifetime of your software platform. The classification through inheritance is problematic if you can't give that guarantee. – Bart van Ingen Schenau Nov 29 '16 at 9:33
0

What you need is a variety of instances. You don't need that same variety in classes, neither the 20, nor even the 4 intermediate subclasses. We should not create (new, sub-) classes when instances of an existing (base) class will do the job.

Each new class you introduce results in tighter coupling than ideal due to invocation of the constructor. To loosen that coupling, we would introduce a factory or dependency injection mechanism as an additional intermediate, and then you'll have all those classes plus extra machinery that is basically unnecessary complexity.

You can accomplish what you need using only the described (3) top level classes plus a factory mechanism. (And you may not even need all 3 of those...)

  • 1
    sorry but no, I do need the 20 classes because they do different things. I don't think a factory is necessary here either given that each class will only use one variant. If I make a CustomComponent it will always be a medium-large ResizeableComponent – kmdreko Nov 29 '16 at 6:51
0

Is using inheritance to set properties bad?

Yes, because you are then using inheritance. Like bikeman868, I too have given up using inheritance and never looked back. Inheritance brings so many problems to your code. You are better off not using it.

In the situation you describe, there are a number of solutions you can adopt without resorting to inheritance. For example, if you are sure that you are likely only to ever need the for size variants:

  • large,
  • medium,
  • medium-large (resizeable), and
  • medium-small

Then you could simply create four interfaces: ILarge, IMedium, IMediumLarge and IMediumSmall and have those 20 components implement one of them.

Of course, in reality, your implementation may not be as simple as this, depending on how much functionality-baggage is involved with these variants. But some form of composition will provide a better solution than either of your inheritance models.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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