Search code examples
pythonoopcomposition

Python Object-Oriented Programming: Composition


I've been learning how to implement composition into my python programming, but I'm struggling to understand why it's preferred over inheritance.

For example, here is my code so far:

class Particle:
   # Constructor (public)
   def __init__(self, _name, _charge, _rest_energy, _mass, _velocity):
       # Attributes (private)
       self.__name = _name
       self.__charge = _charge
       self.__restEnergy = _rest_energy
       self.__mass = _mass
       self.__velocity = _velocity

   # Getter functions (public)
   def getName(self):
       return self.__name

   def getCharge(self):
       return self.__charge

   def getRestEnergy(self):
       return self.__restEnergy

   def getMass(self):
       return self.__mass

   def getVelocity(self):
       return self.__velocity

   # Setter procedures (public)
   def setName(self, _name):
       self.__name = _name

   def setCharge(self, _charge):
       self.__charge = _charge

   def setRestEnergy(self, _rest_energy):
       self.__restEnergy = _rest_energy

   def setMass(self, _mass):
       self.__mass = _mass

   def setVelocity(self, _velocity):
       self.__velocity = _velocity


class Quark:
   # Constructor (public)
   def __init__(self, _name, _charge, _strangeness):
       # Attributes (private)
       self.__name = _name
       self.__charge = _charge
       self.__strangeness = _strangeness

   # Getter functions (public)
   def getName(self):
       return self.__name

   def getCharge(self):
       return self.__charge

   def getStrangeness(self):
       return self.__strangeness


class Hadron:
   # Constructor (public)
   def __init__(self, _name, _charge, _rest_energy, _mass, _velocity, _quarks):
       # Attributes (private)
       self.__particle = Particle(_name, _charge, _rest_energy, _mass, _velocity)
       self.__quarks = _quarks

   # Getter functions (public)
   def getParticle(self):
       return self.__particle

   def getQuark(self):
       return self.__quarks

   def getStrangeness(self):
       _quarks = self.__quarks
       _strangeness = 0
       for _quark in _quarks:
           _strangeness += _quark.getStrangeness()
       return _strangeness

   def getRelCharge(self):
       _quarks = self.__quarks
       _relCharge = 0
       for _quark in _quarks:
           _relCharge += _quark.getCharge()
       return _relCharge

   def getName(self):
       return self.__particle.getName()

   def getCharge(self):
       return self.__particle.getCharge()

   def getRestEnergy(self):
       return self.__particle.getRestEnergy()

   def getMass(self):
       return self.__particle.getMass()

   def getVelocity(self):
       return self.__particle.getVelocity()

   # Setter functions (public)
   def setName(self, _name):
       self.__particle.setName(_name)

   def setCharge(self, _charge):
       self.__particle.setCharge(_charge)

   def setRestEnergy(self, _rest_energy):
       self.__particle.setRestEnergy(_rest_energy)

   def setMass(self, _mass):
       self.__particle.setMass(_mass)

   def setVelocity(self, _velocity):
       self.__particle.setVelocity(_velocity)

I'm not sure if I've gotten the wrong end of the stick here or what, but it seems incredibly wasteful, when I could just inherit from the Particle class.

Am I doing something wrong?


Solution

  • Which you use depends on what relationship you're trying to model.

    Composition isn't always the better option. "Composition over inheritance" is often repeated because inheritance is often abused with the idea that it reduces the amount of code that you need to write. That's entirely the wrong motivation to base your decision on though.

    If you have two classes, A and B, a rough, general guide is:

    • If B is an A, you probably want inheritance.
    • If B has an A, you probably want composition.

    In your case here, from my extraordinarily limited knowledge of particle physics, a Hadron is a Particle, so inheritance is probably a better fit. A Hadron doesn't contain/have a Particle, so I think you're trying to work against the grain by forcing composition here.