Introduction

In our previous article, we looked at - Inheritance vs Polymorphism. Within this article, we will look at another OOP aspect - Inheritance vs Composition.

To explain this our example will be base upon a small ACL builder. This builder build an ACL for either an IOS or Junos based device. I appreciate the full config isn't included, but its adequate for the scope of this tutorial.

Inheritance

Inheritance establishes a relationship between two classes - parent (superclass) and child (subclass).

Child classes keep the attributes and methods of its parent, whilst also adding new attributes or methods of its own. Furthermore, the child class can also override methods of its parent.

Let us look at an example. Here JunosBuilder and IosBuilder extend the base class AccessListBuilder. With each childclass overriding the build_acl method, and inheriting the print_username method.

class AccessListBuilder:
   def print_username(self):
       print("Cyril")

   def build_acl(self, ipaddr):
       raise NotImplementedError("Use a subclass!")

class JunosBuilder(AccessListBuilder):
   def build_acl(self, ipaddr):
       return "set security policies from-zone trust to-zone untrust source-address {}".format(ipaddr)

class IosBuilder(AccessListBuilder):
   def build_acl(self, ipaddr):
       return "access-list acl_permit permit ip host {}".format(ipaddr)
>>> aclbuilder = JunosBuilder()
>>> aclbuilder.print_username()
Cyril
>>> print(aclbuilder.build_acl("10.1.1.100"))
set security policies from-zone trust to-zone untrust source-address 10.1.1.100

>>> aclbuilder = IosBuilder()
>>> aclbuilder.print_username()
Cyril
>>> print(aclbuilder.build_acl("10.1.1.100"))
>>> access-list acl_permit permit ip host 10.1.1.100

Composition

Composition is defined as,

… the act of combining parts or elements to form a whole.

When it comes to OOP and Python, unlike inheritance we do not inherit from a base class. Instead, composition results in relationships being created between classes through the use of instance variables,[1] with these instance variables reference the other objects.

Let's look at our ACL builder, but this time built using composition. As you can see, no inheritance is performed. Instead, we create our builder objects, which are passed into our AccessListBuilder via the set_provider method.

Therefore by using composition is allows for decoupled code as classes and functions can work independently and do not depend on each other.

class JunosBuilder:
    def build_acl(self, ipaddr):
        return "set security policies from-zone trust to-zone untrust source-address {}".format(ipaddr)

class IosBuilder:
    def build_acl(self, ipaddr):
        return "access-list acl_permit permit ip host {}".format(ipaddr)

class AccessListBuilder:
    device_builder = JunosBuilder()

    def print_username(self):
       print("Cyril")

    def set_provider(self, provider):
        self.device_builder = provider

    def build_acl(self, ipaddr):
        print(self.device_builder.build_acl(ipaddr))
>>> aclbuilder = AccessListBuilder()
>>> aclbuilder.print_username()
Cyril

>>> aclbuilder.set_provider(IosBuilder())
>>> aclbuilder.build_acl("10.1.1.100")
access-list acl_permit permit ip host 10.1.1.100

>>> aclbuilder.set_provider(JunosBuilder())
>>> aclbuilder.build_acl("10.1.1.100")
set security policies from-zone trust to-zone untrust source-address 10.1.1.100

References


  1. "Classes and Objects II (Inheritance and Composition) Tutorials ...." https://www.hackerearth.com/practice/python/object-oriented-programming/classes-and-objects-ii-inheritance-and-composition/tutorial/. Accessed 24 Apr. 2019. ↩︎