Less: Better to Use Inheritance or Multiple Classes

LESS: Better to use inheritance or multiple classes

Various Options, No Set Answer

It's going to depend a lot upon your code, your goals, etc., how you get styles to the various elements. Here are some possibilities, each with strengths and weaknesses.

1. Mixin (what you currently do)

LESS

.inputbase() {
/* your base code */
}

.someInput {
.inputbase;
/*some input special code */
}

.someOtherInput {
.inputbase;
/*some other input special code */
}

.andAnotherInput {
.inputbase;
/*and another input special code */
}

CSS Output

.someInput {
/* your base code */

/*some input special code */
}
.someOtherInput {
/* your base code */

/*some other input special code */
}
.andAnotherInput {
/* your base code */

/*and another input special code */
}

If there is more than a line or two of code in .inputbase(), and if it is mixed in more than just a couple of instances, this will be generating a lot of extra code. This is the issue you find yourself facing.

2. Extend a Class

It appears LESS is just about ready to allow for extending mixins, but at present (LESS 1.5) this requires just a class definition, so this:

LESS

.inputbase {
/* your base code */
}

.someInput {
&:extend(.inputbase);
/*some input special code */
}

.someOtherInput {
&:extend(.inputbase);
/*some other input special code */
}

.andAnotherInput {
&:extend(.inputbase);
/*and another input special code */
}

CSS Output

.inputbase, /* this is gone once mixin extending allows .inputbase() extension */
.someInput,
.someOtherInput,
.andAnotherInput {
/* your base code */
}
.someInput {
/*some input special code */
}
.someOtherInput {
/*some other input special code */
}
.andAnotherInput {
/*and another input special code */
}

The advantage is all the base code is not repeated, but what is repeated is the selectors, as they are first grouped together with the base code, then again are output for the individual code. If one likes to keep their code grouped in one selector definition, then this would not be the way to go. Otherwise, this offers a nice way to reduce CSS output.

3. Two Classes (extra html markup you propose)

This one solution you proposed, having two classes (this is because you stated that you do not always want .inputbase applied to an input element).

LESS and CSS Output*

.inputbase {
/* your base code */
}

.someInput {
/*some input special code */
}

.someOtherInput {
/*some other input special code */
}

.andAnotherInput {
/*and another input special code */
}

This does have the least amount of CSS, but it has the disadvantage that it also requires the extra HTML markup of the two classes, <input class="inputbase someInput" /> etc.

4. One Class with Override of Base

This may be better than the above.

LESS and CSS Output

input {
/* your base code */
}

.someInput {
/*some input special code */
/*override input base code if needed */
}

.someOtherInput {
/*some other input special code */
/*no override if not needed */
}

.andAnotherInput {
/*and another input special code */
/*no override if not needed */
}

If most inputs will have the baseinput code, you can simply define your base input code within the input element definition, then just override the properties you don't want in your special css code. This allows for less html with just the single class applied <input class="someInput" />. This will keep both the CSS and the HTML less cluttered, but has the disadvantage of remembering what the base code is and being able to override it if needed.

Summary

What will be best depends too much on the particular circumstances you face. But perhaps the two additional options will help you think through your case. I personally would in most cases opt for #2 or #4, but again, there are applications for #1 and #3 as well.

Why to use Interfaces, Multiple Inheritance vs Interfaces, Benefits of Interfaces?

Interfaces are collection of final static fields and abstract methods (Newly Java 8 added support of having static methods in an interface).

Interfaces are made in situations when we know that some task must be done, but how it should be done can vary. In other words we can say we implement interfaces so that our class starts behaving in a particular way.

Let me explain with an example, we all know what animals are. Like Lion is an animal, monkey is an animal, elephant is an animal, cow is an animal and so on. Now we know all animals do eat something and sleep. But the way each animal can eat something or sleep may differ. Like Lion eats by hunting other animals where as cow eats grass. But both eat. So we can have some pseudo code like this,

interface Animal {
public void eat();
public void sleep();
}

class Lion implements Animal {
public void eat() {
// Lion's way to eat
}

public void sleep(){
// Lion's way to sleep
}
}

class Monkey implements Animal {
public void eat() {
// Monkey's way to eat
}

public void sleep() {
// Monkey's way to sleep
}
}

As per the pseudo code mentioned above, anything that is capable of eating or sleeping will be called an animal or we can say it is must for all animals to eat and sleep but the way to eat and sleep depends on the animal.

In case of interfaces we inherit only the behaviour, not the actual code as in case of classes' inheritance.

Q1. As interfaces are having only abstract methods (no code) so how can we say that if we are implementing any interface then it is inheritance ? We are not using its code.

Implementing interfaces is other kind of inheritance. It is not similar to the inheritance of classes as in that inheritance child class gets the real code to reuse from the base class.

Q2. If implementing an interface is not inheritance then How interfaces are used to achieve multiple inheritance ?

It is said because one class can implement more than one interfaces. But we need to understand that this inheritance is different than classes' inheritance.

Q3. Anyhow what is the benefit of using Interfaces ? They are not having any code. We need to write code again and again in all classes we implement it.

Implementing an interface puts compulsion on the class that it must override its all abstract methods.

Read more in my book here and here

Why is multiple inheritance of interfaces a much less difficult thing to achieve than multiple inheritance from a set of classes?

If you inherit two methods with the same signature from different classes, you have an ambiguity when calling this method. It can be resolved, but it is arguably a messy situation.

If you implement two interfaces with the same method signature, it doesn't matter, because there's still just one implementation to choose from when calling the method.

The diamond problem is an extension of the problem described above, which makes the situation even messier. This problem basically goes away when restricting multiple inheritance to interfaces.

Why should I avoid multiple inheritance in C++?

Multiple inheritance (abbreviated as MI) smells, which means that usually, it was done for bad reasons, and it will blow back in the face of the maintainer.

Summary

  1. Consider composition of features, instead of inheritance
  2. Be wary of the Diamond of Dread
  3. Consider inheritance of multiple interfaces instead of objects
  4. Sometimes, Multiple Inheritance is the right thing. If it is, then use it.
  5. Be prepared to defend your multiple-inherited architecture in code reviews

1. Perhaps composition?

This is true for inheritance, and so, it's even more true for multiple inheritance.

Does your object really need to inherit from another? A Car does not need to inherit from an Engine to work, nor from a Wheel. A Car has an Engine and four Wheel.

If you use multiple inheritance to resolve these problems instead of composition, then you've done something wrong.

2. The Diamond of Dread

Usually, you have a class A, then B and C both inherit from A. And (don't ask me why) someone then decides that D must inherit both from B and C.

I've encountered this kind of problem twice in eight years, and it is amusing to see because of:

  1. How much of a mistake it was from the beginning (In both cases, D should not have inherited from both B and C), because this was bad architecture (in fact, C should not have existed at all...)
  2. How much maintainers were paying for that, because in C++, the parent class A was present twice in its grandchild class D, and thus, updating one parent field A::field meant either updating it twice (through B::field and C::field), or having something go silently wrong and crash, later (new a pointer in B::field, and delete C::field...)

Using the keyword virtual in C++ to qualify the inheritance avoids the double layout described above if this is not what you want, but anyway, in my experience, you're probably doing something wrong...

In Object hierarchy, you should try to keep the hierarchy as a Tree (a node has ONE parent), not as a graph.

More about the Diamond (edit 2017-05-03)

The real problem with the Diamond of Dread in C++ (assuming the design is sound - have your code reviewed!), is that you need to make a choice:

  • Is it desirable for the class A to exist twice in your layout, and what does it mean? If yes, then by all means inherit from it twice.
  • if it should exist only once, then inherit from it virtually.

This choice is inherent to the problem, and in C++, unlike other languages, you can actually do it without dogma forcing your design at language level.

But like all powers, with that power comes responsibility: Have your design reviewed.

3. Interfaces

Multiple inheritance of zero or one concrete classes, and zero or more interfaces is usually Okay, because you won't encounter the Diamond of Dread described above. In fact, this is how things are done in Java.

Usually, what you mean when C inherits from A and B is that users can use C as if it was an A, and/or as if it was a B.

In C++, an interface is an abstract class which has:

  1. all its method declared pure virtual (suffixed by = 0) (removed the 2017-05-03)
  2. no member variables

The Multiple inheritance of zero to one real object, and zero or more interfaces is not considered "smelly" (at least, not as much).

More about the C++ Abstract Interface (edit 2017-05-03)

First, the NVI pattern can be used to produce an interface, because the real criteria is to have no state (i.e. no member variables, except this). Your abstract interface's point is to publish a contract ("you can call me this way, and this way"), nothing more, nothing less. The limitation of having only abstract virtual methods should be a design choice, not an obligation.

Second, in C++, it makes sense to inherit virtually from abstract interfaces, (even with the additional cost/indirection). If you don't, and the interface inheritance appears multiple times in your hierarchy, then you'll have ambiguities.

Third, object orientation is great, but it is not The Only Truth Out ThereTM in C++. Use the right tools, and always remember you have other paradigms in C++ offering different kinds of solutions.

4. Do you really need Multiple Inheritance?

Sometimes, yes.

Usually, your C class is inheriting from A and B, and A and B are two unrelated objects (i.e. not in the same hierarchy, nothing in common, different concepts, etc.).

For example, you could have a system of Nodes with X,Y,Z coordinates, able to do a lot of geometric calculations (perhaps a point, part of geometric objects) and each Node is an Automated Agent, able to communicate with other agents.

Perhaps you already have access to two libraries, each with its own namespace (another reason to use namespaces... But you use namespaces, don't you?), one being geo and the other being ai

So you have your own own::Node derive both from ai::Agent and geo::Point.

This is the moment when you should ask yourself if you should not use composition instead. If own::Node is really really both a ai::Agent and a geo::Point, then composition will not do.

Then you'll need multiple inheritance, having your own::Node communicate with other agents according to their position in a 3D space.

(You'll note that ai::Agent and geo::Point are completely, totally, fully UNRELATED... This drastically reduces the danger of multiple inheritance)

Other cases (edit 2017-05-03)

There are other cases:

  • using (hopefully private) inheritance as implementation detail
  • some C++ idioms like policies could use multiple inheritance (when each part needs to communicate with the others through this)
  • the virtual inheritance from std::exception (Is Virtual Inheritance necessary for Exceptions?)
  • etc.

Sometimes you can use composition, and sometimes MI is better. The point is: You have a choice. Do it responsibly (and have your code reviewed).

5. So, should I do Multiple Inheritance?

Most of the time, in my experience, no. MI is not the right tool, even if it seems to work, because it can be used by the lazy to pile features together without realizing the consequences (like making a Car both an Engine and a Wheel).

But sometimes, yes. And at that time, nothing will work better than MI.

But because MI is smelly, be prepared to defend your architecture in code reviews (and defending it is a good thing, because if you're not able to defend it, then you should not do it).

Is it okay to use repeated inheritance to make classes smaller?

I'd think about using composition for these. So the model consists of Settings, Hierarchy, Information objects.

It does mean you have to include all the setter/getters on the top-level object, which would then delegate to the respective setters/getters in each of those component classes, but at least you can isolate all the handling of them into separate classes without the complexity of an inheritance hierarchy. And it also means those component classes are now free to inherit from anything they like.

Is it a bad practice to have multiple classes in the same file?

I think you should try to keep your code to 1 class per file.

I suggest this because it will be easier to find your class later. Also, it will work better with your source control system (if a file changes, then you know that a particular class has changed).

The only time I think it's correct to use more than one class per file is when you are using internal classes... but internal classes are inside another class, and thus can be left inside the same file. The inner classes roles are strongly related to the outer classes, so placing them in the same file is fine.



Related Topics



Leave a reply



Submit