µObjects: Composition over Inheritance

Inheritance is a code smell.

Composition over Inheritance

This is a known suggestion to improve the maintainability of software. How does this play into microObjects?
Everything has a single responsibility. It's doing A thing; as I talk about in µObjects: Principles to code by.

Composition over inheritance allows µObjects to control thier own behavior. It allows µObjects to have only the behavior; and responsibility; that the object wants. Nothing more and nothing less.

When you inherit - It doens't matter what YOU want YOUR behavior to be; you MUST HAVE ALL the behavior of your ancestor. As an object; you're forced to have the behavior someone else decided at some other time to add to some other object.
This is bad Object Oriented Programming. An object's behavior can change because some other object(s) needed a change.

When you compose an object - You are ensuring that you only have the behavior you want. Even if you are built of other objects that do that behavior for you. Delegation is a great way to have behavior. This is a huge aspect of how µObjects do things. There's a lot of composition to expose related behavior.

The simplest example of this distinction is that if an object adds a new method.

public abstract class Foo : IFoo{
  IBar Bar(){ ... }
  IBarFoo abstract FooBar();
  IFooBar virtual SomeNewMethod() { ... }
}
public abstract class YourClass : Foo{
  override IBarFoo FooBar(){ ... }
}

This SomeNewMethod is immediately available to consumers of YourClass. You didn't give this to your consumers, but they can use it. If you then try to implement a new form of it - YOUR consumers broke because they're expecting the behavior of Foo#SomeNewMethod, not YourClass#SomeNewMethod.

Composition is what saves you from this unknown addition of behavior. It protects your consumers from changes in objects they may never know about. It protects your development from having to create a YourClass#OtherNewMethod to implement behavior your class should have.

The policy we've been following in µObjects development is the rule of three. Once is done for the case. Twice is recognized and maybe a little more generic; but kept as two distinct implimentations. The third time we have duplicate code - we look if it can be brought together cleanly.
This requires understanding of duplicate code. The short form is that duplicate code isn't code that looks the same; it's code that changes for the same reason.
If we have 3 instances of code that will need to change for the same reason - BAM! Refactor the hell out of it.
This will create inheritance in the code. Right off the bat I said that 'Inheritance is a code smell'. I think so. I don't think every smell will be gone. It's a trade off. There's the smell of having to make a change in 3+ places; or using inheritance. It's at the three mark that we can reasonibly understand how to effectively make it generic; make it a base class. It still smells. Points out that, perhaps, there's a different abstraction missing that'd collapse it back down into a single class.
Inheritance smells less than having to change code in multiple places for the same reason. This is why we refactor into a base class. Even if the code was identical, but would change for different reasons - It's not duplicate code.

µObjects don't want inheritance. They do a single thing. If you need to inherit behavior; you're likely doing multiple things, which should return to being multiple objects.
µObjects don't want to inherit behavior. They want to do only their one thing. Any behavior they need to delegate will be done through a composition of other objects; not an inheritance of other behaviors.

Show Comments