What is the actual difference in using composition versus inheritance?
I am trying to learn inheritance versus composition the hard way. So far, I have understood that composition involves using object reference inside a class. So I asked myself, why did not we choose to use composition in Django for, say, ModelViewSet, like:
class MyViewSet:
queryset = ...
viewset = ModelViewSet()
and then, any get request will be redirected to MyViewSet.list(self) having a call to self.viewset.list(self)?
Maybe this SO post about Inheritance vs Composition answers the "difference" part of your question.
any get request will be redirected to MyViewSet.list(self) having a call to self.viewset.list(self)
And who would be calling self.viewset.list(self)? MyViewSet is an "empty" class without its own methods at all. There is no MyViewSet.list that could be called. Unless you want to write a def list(self): ... for each of your view classes every time?
No, if you want MyViewSet to behave in some way without writing that behaviour into it, it must inherit something from somewhere. Inheritance makes perfect sense for these cases. You inherit a complete ViewSet with some very extensive behaviour, and only customise its behaviour by adjusting a couple of attributes, or maybe overriding a method or three.
Composition makes more sense when your class "isn't a thing". In the case of ViewSets, the ViewSet isn't a model or queryset. It would make no sense for the ViewSet to have any methods that make a model or queryset work. Thus you're setting the queryset attribute to an instance of the particular queryset it'll need, but you're not letting it inherit that queryset. Because the ViewSet doesn't need to behave like a queryset, it just needs to get access to an object that does.
if someone would like to create own class using composition with your MyViewSet
class OtherViewSet:
queryset = ...
viewset = MyViewSet()
then it would have to write self.viewset.viewset.list(self) (and this makes it too long for me)
And using inheritance in OtherViewSet and MyViewSet it would always have self.list(self)
In programming, as in many other fields, there are many “Taliban” who invoke their “mystical recipes” without doing the slightest analysis of what they proclaim, beware of them, beware of “preferring composition to inheritance”, they are different concepts, each with its own “space of use”.
Inheritance is basically “syntactic sugar.” We inherit a class model to avoid having to rewrite identical attributes and methods. On the other hand, it allows us to group objects of different types under the same umbrella. For example, instead of having:
class Cow():
def __init__( self, p ):
self.price = p;
def getPrice( self ):
return self.price;
def feed( self ):
print( "feeding the cow" )
class Tractor():
def __init__( self, p ):
self.price = p;
def getPrice( self ):
return self.price;
def plow( self ):
print( "plowing the land" );
we use:
class SomethingWithPrice():
def __init__( self, p ):
self.price = p;
def getPrice( self ):
return self.price;
# If the parameters are the same, we can even
# omit rewriting the constructor.
class Cow( SomethingWithPrice ):
def feed( self ):
print( "feeding the cow" )
class Tractor( SomethingWithPrice ):
def plow( self ):
print( "plowing the land" );
and then:
class Main():
def __init__( self ):
valueOfMyBelongings = 0
# we group together very different objects (...or not)
myBelongings = [ Cow( 7.0 ), Tractor( 15.7 ) ]
for obj in myBelongings:
valueOfMyBelongings += obj.getPrice()
print( valueOfMyBelongings )
Main()
Composition, on the other hand, refers to the use of objects within other objects, which help implement its functionality (this can be created by refactoring the code).
we can use:
class Cow():
def __init__( self, weight, pricePerWeight ):
self.weight = weight;
self.pricePerWeight = pricePerWeight;
def getPrice( self ):
return self.pricePerWeight * self.weight;
or modify it using composition:
class Cow():
def __init__( self, weight, pricePerWeight ):
self.weight = weight;
self.pricePerWeight = pricePerWeight;
def getPrice( self ):
return PriceCalculator().getPrice( self );
class PriceCalculator():
def getPrice( self, cow ):
return cow.pricePerWeight * cow.weight;
class Main():
def __init__( self ):
print( Cow( 700, 10 ).getPrice() )
Main()
Now, the million-dollar question is: “When should we resort to composition?” The answer, as is often the case in programming, is not precise.
As a general rule, I would say that, in principle, if a portion of the internal code is not only useful for the host class but also for others, it is clear that we should separate it by creating a new class with the corresponding method (to reuse it). But we could also use composition to “clean up” our code and simplify its readability, generally associated with an internal object with many lines of code (“many lines,” a rather elusive concept). There are probably other considerations, but these are the ones that come to mind.
Note that in the Cow class, the object PriceCalculator is instantiated with each call to getPrice(), but it could also be received as a parameter.
Context:
Composition and Inheritance are not always interchangeable;
Similar kinds of questions heavily depend on the language and each language specific point of view and approach on Object-Oriented design;
In general:
Composition (as the name suggest) is "object A has-a object B" - example: you have a Sensor object that "has-a" Alert, and the runloop triggers the alert every some time;
Inheritance (again name suggests it) is "object AB is-a object A" - think about the example and you can see, if you did some object thinking for some time, that you could do the same previous example in a bit of a different way, like let's say an AlertingSensor, which inherits from Sensor, something like that.
Is there a right or wrong?
Not necessarily, how I see it and how I could imagine Django (I have some exp. since 0.96 until they added OO views) reasoned is:
In Python, composition is easier to do and easier to think of, and it's one of the main strengths of Python, its dynamicity, gives the possibility to easily create plug-in systems that are straightforward to think of and handle;
Inheritance is a bit more complex cognitive model BUT it allows you to "enforce a way" and have a stricter model, which makes a lot of sense for an open source web framework, why:
- Imagine ppl assigning whatever component to self.alert and then complaining online that the alerting does not work, and it's always because their alert object is not done right.
So for a simpler project, or an internal system (not public), a plug-in system is usually easier to think of and more flexible. With composition, the failure modes are more varied and harder to trace: wrong type, missing method, subtle interface mismatch, silent no-ops. It leans on being more about Encapsulation than Abstraction (Inheritance and Polymorphism are next-to and next-next-to of Abstraction).
Looking at other answers, inheritance is ABSOLUTELY NOT "syntactic sugar". It is one of the four pillars of object oriented design: Encapsulation, Abstraction, Inheritance, Polymorphism. It is a way to define object behaviour as deterministically as possible, which in Python is done by creating a contract enforced by the MRO.
yes, perhaps I was too brief in explaining what inheritance is (although I never said it's just syntactic sugar), I've made a small modification to improve it (it wasn't and isn't my intention to go into depth), thanks for the “slap on the wrist” :)