2.4 SOLID Principles

Introduction

The SOLID design principles are a set of principles for the design of classes, functions and programs in Object-oriented software development. SOLID is an acronym for the five core principles:

  1. Single purpose
  2. Open/Close principle
  3. Liskov Substitution
  4. Interface Segregation
  5. Dependency Inversion

[S]ingle Purpose

When designing a function or class, it should serve a single purpose.

This principle is closely related to that of Strong Cohesion. It is important, when designing functions and classes, to be very modular in your approach. Functions and classes should be specialized in terms of cohesion and focus on solving a specific problem, but generic in the solution to the problem, so as to be useful in solving similar problems.


[O]pen/Close Principle

An abstract class should be OPEN to extension through inheritance and CLOSED to modification.

When specific situations arise where code is needed to be added to accommodate a certain condition, it should be added to the appropriate child class, and not tacked on to the parent class. The functionality of the parent class should never be modified to accommodate functionality relating to the specialization of a child class. For example, if you find yourself putting in an IF statement into the parent class which would only run code in the case of a specific child class, you have broken the Open/Close principle.


[L]iskov Substitution

A pointer to an object of a parent class should be able to be replaced with a pointer to a child object without affecting functionality of the code.

When polymorphism is used, the purpose of the parent function should be maintained. For example, if we have a parent class "Motor Vehicle" and we have the child classes "Car" and "Motor Bike". If the two child classes overload the engine_start() function, it should not result in anything other than starting a motor. The idea of the Liskov Substitution principle is that you should be able to substitute a variable pointing to the parent with a variable pointing to a child of the parent, and have the code still work as expected. This cannot happen if the parent's functions are overloaded in a way which is inconsistent with the intention or purpose of the parent function.


[I]nterface Segregation

Only fundamentally necessary function definitions should be included in an interface.

When defining interfaces, rather create multiple interfaces with fewer required functions to implement than a single interface with a large number of functions to implement. In addition, only specify the functions that are absolutely necessary to implement, and no more.


[D]ependancy Inversion

Abstract classes should never depend on implementation details of concrete classes.

The goal of the dependency inversion principle is to decouple application glue code from application logic. Thus, the concrete child classes should be able to be interchanged without the abstract parent classes being affected.


Exercises

In each of the following code examples, determine the design principle which has been broken, describe why this is the case, and provide the corrected code which adheres to all design principles.

  1. Example One

    import time

    def calculate_age(): # Get date of birth from the user dob = "" while dob == "": user_input = raw_input("What is your date of birth? YYYY-MM-DD? ") if len(user_input) == 10: dob = user_input else: print "Please enter in your date of birth in the correct format."

    # Split the date into its parts
    year = dob[:4]
    month = dob[5:7]
    day = dob[8:10]
    
    # Get Today's Date
    this_year = time.strftime("%Y")
    this_month = time.strftime("%m")
    this_day = time.strftime("%d")
    
    # Calculate Number of Years Difference
    diff_years = this_year - year
    diff_month = this_month - month
    diff_day = this_day - day
    if (diff_day < 0):
        diff_month = diff_month - 1
        diff_day = 30 + diff_day
    if (diff_month < 0):
        diff_years = diff_years - 1
        diff_month = diff_month + 12
    
    # Display the precise age
    print "You are %d years, %d months, and %d days old" % (diff_year, diff_month, diff_day)
    
  2. Example Two

    class User:

    username = ""
    password = ""
    type = ""
    
    def __init__(self, username, password, type):
        self.username = username
        self.password = password
        self.type = type
    
    def get_username(self):
        return self.username
    
    def authenticate(self, password):
        if self.password == password
            return True
        else:
            return False
    
    def get_type(self):
        return self.type
    
    def has_authority(self):
        if self.get_type() == "Admin":
            return True
        else:
            return False
    
    def change_password(self, old_password, new_password):
        if self.password == old_password:
            self.password = new_password
        else
            return False
    

    class Administrator(User):

    admin_access_level = 0
    
    def set_level(self, access_level):
        self.admin_access_level = access_level
    

    class Employee(User):

    email = ""
    office_tel = ""
    
    def set_email(self, email):
        self.email = email
    
    def set_office_tel(self, tel):
        self.office_tel = tel
    

    users = [ Administrator("bob", "builder", "admin"), Employee("eddie", "banana", "employee"), Employee("fred", "chopsticks", "employee") ] for u in users: print "User: %s" % (username) if u.has_authority(): print "Admin access granted" else: print "Limited access"

  3. Example Three

    class Device:

    device_id = 0
    type = ""
    
    def __init__(self, id, type):
        self.device_id = id
        self.type = type
    
    def get_cick(self):
        if (self.type == "PC" or self.type == "Server"):
            return get_mouse_click()
        elif (self.type == "Touch screen"):
            return get_touch()
        else:
            return False
    

    class PC:

    def __init__(self, id):
        self.device_id = id
        self.type = "PC"
    

    class iPad:

    def __init__(self, id):
        self.device_id = id
        self.type = "Touch screen"
    

    devices = [ PC(1), iPad(2), PC(3) ] for d in devices: d.get_click()

Continue to Module 3: Databases


Comments

comments powered by Disqus