Clean Code Design Patterns Principles

Notes about Design Patterns & Clean Coding


Last Updated: June 06, 2021 by Pepe Sandoval



Want to show support?

If you find the information in this page useful and want to show your support, you can make a donation

Use PayPal

This will help me create more stuff and fix the existent content...


Design Patterns Clean Code Principles

  • A design pattern is named a solution to a problem in a context

  • A pattern is a solution used when facing certain problem in software

  • Types of patterns:

    • Creational patterns: help you create objects
    • Structural patterns: help you setup communications pathways between objects
    • Behavioral patterns: help you partition the behaviors of your system into discrete classes
  • All patterns are used for management of dependencies, their purpose is to figure out how to separate the elements of your system, they assist with the design of the system

The Command Pattern

  • Most times this pattern is composed of an interface named Command and a single method named execute

  • It is way to decouple what is done from who does it (Separate actors from actions)

The Command Pattern

  • We can use it to separate what a task does from who does it and also when it gets done

  • A command is represented with an object so it could save state/information about what it did to undo or redo, so the command pattern is a good way to implemented undo functionality

  • The actor model/Run-to-Completion model for threads The way a thread waits is that it puts a command on the list that checks an event and if the event hasn't occurred the command put itself on the list otherwise puts a command with an actual action

Run-to-Completion Pattern

Factories

  • The purpose of the factory pattern is to provide a mechanism for the independent deployable applications to create the objects they use without depending on those objects

  • The factories are trying to keep the people who use the objects and the people who create the objects from knowing about each other so they are not interfering over each other

Abstract Factory Pattern

Factory Method Pattern

  • All factory patterns have initialization problem, if you want to initialize it you will need to pass data that can be interpreted by the implementation

Strategy & Template Method Patterns

  • If you have a problem you don't look up a pattern that solves it, usually you know the patterns so when you start coding or growing your application you can see how the patterns fit into the application

  • Both Strategy & Template Method Patterns solve the same problem but have different implementation costs and benefits

  • These patterns allows us to separate high level policy from a set of low level details/implementation, this is done by having the high level policy delegate through an interface and derivatives that implement that interface (polymorphism)

Strategy & Template Method Patterns

  • Costs and benefits

    • Template Method: Ease of creation because you only need to create one object that makes it faster and smaller, low and high level policy are tightly coupling due to direct inheritance
    • Strategy: Need to create two objects and hook them together which can make it a little slow and cost more memory but it is more flexible because high level policy and low level implementation can be created separately and at different times (reduces coupling), this means strategies can be hot swapped
  • Don't just use a pattern because you can, use them when they solve a problem

Finite state machine

  • Finites state machine:

    • It is a machine that has a finite number of states
    • All computers have a finites number of states so all computer programs are finite state machines
    • Every program that handles a sequence of events is a FSM
  • Subway Turnstile FSM (Given=State, When=Event, Then=Invoke Function) FSM

  • GUI as a FSM

    • GUIs are not usually modeled as FSM but they can greatly benefit for a formal analysis of their states, events, transitions and actions
    • GUIs hidden state from users but it needs to do it by carefully managing states, events, actions and transitions
    • GUIs have means and tabs of screens, both have states and user actions are events that trigger actions and transitions of state
  • We should be able to put a FSM in its own module, isolated from the rest of the system, no need to add all the control logic into the program itself.

  • A FSM benefits from an implementation that separate the FSM from it's actions and events

  • State Machine representations

    • Black box that accepts as input events and produces actions as outputs
    • It is a way for a system to separate what it does from how it does it
      • The how part doesn't need to know what is operating and the what part doesn't need to know how is being operating
  • The separation of how (to do something) and what (is being done) is an essential aspect of good SW design so we can separate operation from policy

FSM

  • The state pattern
    • Class that has methods that represent events, these are the methods that will be called by outside world when events happen
    • The calls also has private methods for the actions
    • It has a a state attribute that is settable (set_state), holds reference to a state interface
      • state interface has event functions (again) but this time it takes as argument a reference to the original class so it can call original event methods
      • We have derivatives of the interface for each state, each derivative implement the event functions, call the appropriate actions using the reference to the original class and change state attribute by calling set_state
    • public event methods just delegate using the state variable so the right method is excited
    • All Derivatives must know about each other and know about original class, even interface must know about its derivatives

FSM

# python3 -m unittest test_fsm.py
from unittest import TestCase
from abc import ABC


class TurnstileState_Interface:
    def __init__(self):
        pass
    def person_pass_event(self, fsm):
        pass
    def coin_event(self, fsm):
        pass

class OneCoinTurnstileState_Locked(TurnstileState_Interface):
    def __init__(self):
        pass
    def person_pass_event(self, fsm):
        #print("person_pass_event in OneCoinTurnstileState_Locked state")
        fsm.alarm()
    def coin_event(self, fsm):
        #print("coin_event in OneCoinTurnstileState_Locked state")
        fsm.set_state(TursntileFSMOneCoin.unlocked_state)
        fsm.unlock()

class OneCoinTurnstileState_Unlocked(TurnstileState_Interface):
    def __init__(self):
        pass
    def person_pass_event(self, fsm):
        #print("person_pass_event in OneCoinTurnstileState_Unlocked state")
        fsm.set_state(TursntileFSMOneCoin.locked_state)
        fsm.lock()
    def coin_event(self, fsm):
        #print("coin_event in OneCoinTurnstileState_Unlocked state")
        fsm.thankyou()

class TursntileFSM(ABC):
    def __init__(self):
        self.state = None

    def person_pass_event(self):
        self.state.person_pass_event(self)

    def coin_event(self):
        self.state.coin_event(self)

    def set_state(self, state):
        self.state = state

    def alarm(self):
        pass
    def lock(self):
        pass
    def unlock(self):
        pass
    def thankyou(self):
        pass

class TursntileFSMOneCoin(TursntileFSM):
    unlocked_state = OneCoinTurnstileState_Unlocked()
    locked_state = OneCoinTurnstileState_Locked()

    def __init__(self):
        super(TursntileFSMOneCoin, self).__init__()
        self.set_state(TursntileFSMOneCoin.locked_state)
        self.actions = []

    def alarm(self):
        #print("alarm")
        self.actions.append("alarm")
    def lock(self):
        #print("lock")
        self.actions.append("lock")
    def unlock(self):
        #print("unlock")
        self.actions.append("unlock")
    def thankyou(self):
        #print("thankyou")
        self.actions.append("thankyou")


class FSMTest(TestCase):
    def test_fsm_normal_operation(self):
        fsm_obj = TursntileFSMOneCoin()
        fsm_obj.coin_event()
        self.assertEqual(fsm_obj.actions, ["unlock"])
        fsm_obj.person_pass_event()
        self.assertEqual(fsm_obj.actions, ["unlock", "lock"])

    def test_fsm_forced_entry(self):
        fsm_obj = TursntileFSMOneCoin()
        fsm_obj.person_pass_event()
        self.assertEqual(fsm_obj.actions, ["alarm"])

    def test_fsm_double_pay(self):
        fsm_obj = TursntileFSMOneCoin()
        fsm_obj.coin_event()
        fsm_obj.coin_event()
        self.assertEqual(fsm_obj.actions, ["unlock", "thankyou"])

    def test_fsm_n_pay(self):
        fsm_obj = TursntileFSMOneCoin()
        for i in range(5):
            fsm_obj.coin_event()
        self.assertEqual(fsm_obj.actions, ["unlock", "thankyou",
                                          "thankyou", "thankyou",
                                          "thankyou"])

    def test_fsm_n_pay_then_pass(self):
        fsm_obj = TursntileFSMOneCoin()
        for i in range(5):
            fsm_obj.coin_event()
        fsm_obj.person_pass_event()
        self.assertEqual(fsm_obj.actions, ["unlock", "thankyou",
                                          "thankyou", "thankyou",
                                          "thankyou", "lock"])

    def test_fsm_normal_operation_two_user_pass(self):
        fsm_obj = TursntileFSMOneCoin()
        fsm_obj.coin_event()
        self.assertEqual(fsm_obj.actions, ["unlock"])
        fsm_obj.person_pass_event()
        self.assertEqual(fsm_obj.actions, ["unlock", "lock"])
        fsm_obj.coin_event()
        self.assertEqual(fsm_obj.actions, ["unlock", "lock", "unlock"])
        fsm_obj.person_pass_event()
        self.assertEqual(fsm_obj.actions, ["unlock", "lock", "unlock", "lock"])

SMC Parser

  • Backus Naur Form BNF is a notation used to describe computer languages

    • Notation:
      • ::= composed of
      • | Or
      • * Many
    • A terminal is something that doesn't need to be defined. e.x. :
    • <header> Name followed by a colon (:) followed by another name
  • Lexical Analyzer (Lexer) takes the stream of characters and transforms it into a stream of tokens

SMC example

Test fisrt when you can, test after when you must but test

Visitor Pattern

Classical Visitor Pattern

  • Behavior which is polymorphic to a hierarchy but that does not belong in that hierarchy

  • You have a hierarchy of classes and you want to put a new behavior on that hierarchy, each time you call the behavior on a particular instance you want a particular behavior (polymorphic) but you don't want the code that implements that behavior as part of the hierarchy

  • You add to the base class a method called accept which takes as argument an instance of a visitor (interface) that has a visti method that takes as argument an instance of the derivatives classes (you can have one visit method for each of the derivatives of the hierarchy or a single visit method that handles the different derivatives)

  • accept is abstract in the base class and in the derivatives it is implemented identically by calling the visit method and passing as argument the current instance

  • The visitor patterns allows to add polymorphic functionality/behavior to existing hierarchies without changing the hierarchies (in other words by just adding one method accept to base class and one visit to each derivative)

Visitor

class Modem:
    def __init__(self):
        pass
    def send(self):
        pass
    def recv(self):
        pass
    def dial(self):
        pass
    def hangup(self):
        pass
    def accept(self, modem_visitor):
        pass


class HayesModem(Modem):
    def __init__(self):
        pass
    def send(self):
        print(f"{self.__class__.__name__}", "send")
    def recv(self):
        print(f"{self.__class__.__name__}", "recv")
    def dial(self):
        print(f"{self.__class__.__name__}", "dial")
    def hangup(self):
        print(f"{self.__class__.__name__}", "hangup")
    def accept(self, visitor):
        print(f"{self.__class__.__name__}", "accept", f"{visitor.__class__.__name__}")
        visitor.visit_hayes(self)


class USRModem(Modem):
    def __init__(self):
        pass
    def send(self):
        print(f"{self.__class__.__name__}", "send")
    def recv(self):
        print(f"{self.__class__.__name__}", "recv")
    def dial(self):
        print(f"{self.__class__.__name__}", "dial")
    def hangup(self):
        print(f"{self.__class__.__name__}", "hangup")
    def accept(self, visitor):
        print(f"{self.__class__.__name__}", "accept", f"{visitor.__class__.__name__}")
        visitor.visit_usr(self)


class ModemVisitor():
    def __init__(self):
        pass
    def visit_hayes(self, visitor):
        pass
    def visit_usr(self, visitor):
        pass

class UnixModemConfiguration(ModemVisitor):
    def __init__(self):
        pass
    def visit_hayes(self, hayes_visitor):
        print(f"{self.__class__.__name__}", "visit_hayes called with access to", f"{hayes_visitor.__class__.__name__}")
        hayes_visitor.dial()
        hayes_visitor.hangup()
    def visit_usr(self, usr_visitor):
        print(f"{self.__class__.__name__}", "visit_usr called with access to", f"{usr_visitor.__class__.__name__}")
        usr_visitor.send()
        usr_visitor.recv()

class WindowsModemConfiguration(ModemVisitor):
    def __init__(self):
        pass
    def visit_hayes(self, hayes_visitor):
        print(f"{self.__class__.__name__}", "visit_hayes called with access to", f"{hayes_visitor.__class__.__name__}")
        hayes_visitor.send()
        hayes_visitor.recv()
    def visit_usr(self, usr_visitor):
        print(f"{self.__class__.__name__}", "visit_usr called with access to", f"{usr_visitor.__class__.__name__}")
        usr_visitor.dial()
        usr_visitor.hangup()


myusr_modem = USRModem()
myhayes_modem = HayesModem()

print("\nLinux")
os_modem_visitor = UnixModemConfiguration()
myusr_modem.accept(os_modem_visitor)
myhayes_modem.accept(os_modem_visitor)

print("\nWindows")
os_modem_visitor = WindowsModemConfiguration()
myusr_modem.accept(os_modem_visitor)
myhayes_modem.accept(os_modem_visitor)
  • The visitor pattern creates a dependency cycle: The base class depends on the visitors and the visitor depends on the derivatives, so any change to any class in the cycle requires you to recompile and redeploy all the classes in the cycle and also any other class outside the cycle that uses the classes in the cycle

  • Acyclic Visitor is an implementation of the visitor patter used with type checks (instanceof) needed for statically typed languages, used when we suspect more derivatives willl be added to the hierarchy

  • There are Two hierarchies one hierarchy is being visited and one hierarchy is doing the visiting, these form a matrix of functions

Visited & Visiting

The Observer Pattern

  • Used when you have an object that can change for various reason and another object(s) wants to be notified of those changes, it is a formal way of creating callbacks

  • We need to call the register method and pass as instance the class that implements the observer interface when the object that detects event notices a change it calls the notify that must loopd through all the observers registered calling the update on each one

  • pull observer: when the callback is called the observer it pulls data from the object it's observing

    • Better for when we want to share complex data objects
  • push observer: when the call notify we pass data which is passed to the update method so the observed object pushes data to the observer

    • You can see data mismatch due to pushing of data so if timing is important this must be considered
    • Used when you want to push a hint of the data
  • We have two objects: one that calculates a value the cause and other which uses the value the effect

    • In a traditional way the cause calls the effect once the value is calculated and the value is passed to the effect directly. In this case the effect is passive because it just waits to be called by the cause. The cause depends upon the effect at compile time because the cause calls it
    • We can also make the effect call the cause to get the value once the cause notifies it has calculated the value, in this case the cause depends upon the cause ta compile time

Observer

The names of good things loose their meaning because people steal those names in order to steal the good reputation. When structured, objects, service-oriented, agile, functional, modular were considered good many frameworks and other things added structured, object-oriented, service-oriented, agile, functional, modular to their names

MVC & MVP/MVVM

  • Pattern used in the small

  • Model Object that has data that is changing due to interactions

  • View understands how to represent the data in the model

  • Controller understands how to take inputs from user and convert them to commands the model can understand

  • The controller sends commands to model and if data changes the model updates the view

  • Presenter object that gathers, organizes all of the model data, format it if necessary so the only job of the view is just get data out of the presenter into the GUI or other display method

    • If there is a button the presenter must know the name and decide if it is enabled or not (which might represent in a Boolean)

MVC

The Singleton & Monostate

  • Singleton is used when you only want to create one object on your system

  • Monostate is a single but transparent to the users, you use it when you don't want them to know there will only be one object

    • It can be implemented with a class that has only static variables (class variables) but public methods. Called monostate because there is only only instance of the state (of the attributes)
    • Objects with no actual data because the static/class variables are shared among all instances
    • The policy/methods used to manipulate the static/class variables can be stablished by creating different sub-classes of the monostate classes

The Null Object

  • Class that derives from another class that implements all the methods to do 'nothing' in the right way for the base class
  • It is an object that should be able to pass through the system without consequences.
  • It's implementation requires a thorough and deep understanding of the system to so 'nothing' properly
  • Used to eliminate most of the null/None checks in your system
class Employee:
    def __init__(self, name):
        self.name = name
        self.salary = 0.0
        self.pay = 0.0
        self.deductions = 0.0
    def is_pay_day(self):
        # Here we would have logic to determine if it is really a payday
        return True
    def calculate_pay(self):
        # Here we would have logic to calculate pay
        self.pay = self.salary*30.0
        print(f"Calculated salary {self.salary}")
    def calculate_deductions(self):
        # Here we would have logic to calculate deductions
        self.deductions = self.salary*0.30
        print(f"Calculated deductions {self.deductions}")
    def get_gross_pay(self):
        return self.pay - self.deductions
    def send_pay(self):
        # Here we would have logic to send pay
        print(f"Sending gross pay {self.get_gross_pay()} to {self.name}")

class NullEmployee(Employee):
    def __init__(self):
        super(NullEmployee, self).__init__("")
    def is_pay_day(self):
        return False
    def calculate_pay(self):
        pass
    def calculate_deductions(self):
        pass
    def get_gross_pay(self):
        return 0.0
    def send_pay(self):
        pass

class PayRollDB:
    def __init__(self):
        pass
    def get_all_employees_ids(self):
        print(f"Here the code would query the DB to get a list of all the employee IDs")
        return [None]
    def find_employee_by_id(self, eid):
        print(f"Here the code would fetch data from the DB using ID={eid} and create Employee object with that data, if employee not found would create NullEmployee")
        #data_from_db = {"name": "Pepe"}
        data_from_db = None
        if data_from_db:
            e = Employee(data_from_db.get("name"))
        else:
            e = NullEmployee()
        return e

class PayRoll:
    def __init__(self, db):
        self.employee_db = db
        self.employee_ids = self.employee_db.get_all_employees_ids()
    def calculate_payroll_for_employees(self):
        for eid in self.employee_ids:
            e = self.employee_db.find_employee_by_id(eid)
            if e.is_pay_day():
                print(f"It is payday for employee {e}")
                e.calculate_pay()
                e.calculate_deductions()
                e.send_pay()


my_db = PayRollDB()
my_payroll_app = PayRoll(my_db)
my_payroll_app.calculate_payroll_for_employees()

Proxy

  • Used when two objects need to communicate
  • The Proxy creates a data structure with a message and uses a mechanism to transmit that message (for example network API) there must be a listener to this message that will take the appropriate actions

Proxy example

Decorators

  • The proxy belongs to the decorators family, which are a family of patterns that have the intention to add functionality to classes or hierarchies without significantly affecting the hierarchy

  • The decorators have the following structure:

    • you have the Base interface with all the methods that need to be implemented
    • There are one or more implementations (imp) of the Base interface
    • There is a decorators which is an implementation of the Base interface that holds an instance of one of the imp with the purpose of adding extra functionality to this specific implementation
TODO Add modem decorator example

Facade

  • It has the intention to imposing a policy upon another set of objects (constraint the way the objects communicate with each other and/or the outside world)

  • If you have a group of objects that collaborate to provide a function and client that wants to use this function, the Facade will interact with the group of objects and provide an interface to the client that allows the communication with the group of objects but also constrains the kind of communication (the facade is imposing a policy)

  • The facade imposes policy upon a group of low level implementation classes, so the facade has control over what clients are allowed to ask for and how low level classes are allowed to respond

Facade example

Mediator

  • Imposes policy on the communication between objects (Imposes policy from behind the scenes)

  • It works behind the scenes to impose a policy on a group of objects, neither the objects nor the clients of those objects know the mediator exists

  • A common implementation includes an object that holds a reference to two objects, catches events from one object and uses the other object to perform an action

Mediator example

Flyweight

  • Uses when we have a large number of objects that can shared a certain amount of fixed state
  • Used to avoid duplication of state or data
  • Class that hold all the common information for multiple objects (E.g. a object that holds all the information of a specific lawnmower while multiple objects hold the serial number and other data specific to the actual instance and all those share the Flyweight)

Memento

  • It is used when an objects wants to share a record of its internal state to some other part of the system, the system can store this record and then had it back to the originating object, this object can reconstitute itself from this recorded state or the system can also create a new objects from the recorded state but remains private to the originating client

  • The Memento object saves/contains state of another object with the intention to go back to that state or create a new object using that state (E.g. if we want to save state of a chess board, we create a memento with this info then we can use this memento to create a new board or make the original board have this state)

  • A good implementation of the memento makes the memento an object that can only be used by the class that creates it but any other object can store it or hold on to it

TODO Memento board example

Extension

  • Part of the visitor family, so it allows to add behavior to existing hierarchy without changing it

Extension example

TODO Extension bill of materials example

Bridge

  • When you have a group of objects that can be represented by different possible inheritance hierarchies

  • Degree of freedom a hierarchy of classes gives freedom to the programmers to implement new functionality, we have a base class and we can implements one or more derivatives, this is a degree of freedom

  • Used to avoid m*n type of problems and convert them into m+n problem so instead of having derivatives of derivatives we join two hierarchies (Two strategy patterns)

Bridge example

  • Binds multiple strategies into a chain of stages/ stations and each stage performs a task as pass to the next one but it doesn't know anything about the next station

  • It help us deal with multiple degrees of freedom, each degree of freedom has its own inheritance hierarchy

Chain of Responsibility

  • Used when you have a client that can be served by many different services and want to hide to the client what service(s) is doing the job

  • We pass an object through a chain of instances that look at an object and determine if it should apply itself or not, then forwards to next instance

  • Checks an object to see if a stage of processing should be invoked, if it shouldn't be invoked it just passes to next stage, if it should be invoked does calculations and decides if it should pass the object to next stage

Chain of Responsibility example

Interpreter

  • Used when you have a set of business rules that you want to be so flexible that users can modify them without recompiling and redeploying the system

All problems in software can be solved by adding another layer of indirection

  • Assumes you have invented or using some languages that allows you to capture some part of the application and you have written a compiler that translates that language into an abstract syntax tree

Iterator

  • It is a way of hiding containers of objects and representing them instead as a flow or stream of objects

  • Lazy list is a list whose elements are only evaluated when they are used, they don't exist before that

  • The iterator pattern is equivalent to a generator in python and is used to create lists that appear to be infinite but that are evaluated lazy

  • The iterators is an object that returns the next element in a lazy list

Adapter

  • Provides a way of interaction, allowing two objects that don't know about each other to effectively be used together to perform a combined task

  • We should try to name our interfaces for the services that they offer to the classes that use the interface

  • Used when you don't have access to a class but want to add functionality to that class, e.x. the Light class is part of a library but you want to use it alongside a Button class you coded so you use an adapter that implements the interface used by the Button and also delegates to the Light Object

  • The process to setup an adapter usually needs to go several steps since you need to pass or set the object that will be used to delegates tasks

  • The object form of the adapter is the one that uses a new separate object that binds other objects together

Adapter Object form example

  • The class form of the adapter is the one that uses a class that inherits from the separate object in order to bind them together which make it more rigid due to inheritance

Adapter class form example

References

Want to show support?

If you find the information in this page useful and want to show your support, you can make a donation

Use PayPal

This will help me create more stuff and fix the existent content...