Design principles and Design Patterns

Table of Contents

These are my notes while watch this Youtube channel.

Cohesion and coupling

  • Separate code that changes from that does not change.
  • Program to interfaces (abstract class) instead of implementations.
  • Prefer composition over inheritance.
    • Think about the bridge pattern
  • Delegation
    • Call a class member (refer to item 3) to do things

SOLID:

  • Single responsibility
    • High cohesion
    • Break a long class into small classes. But this will introduce coupling.
  • Open/close
    • Open for extension / close for modification
      • Instead of modifying the original class to add some additional type support, use the abstract class and a new inheritance is preferred.
  • Liskov substitution
    • When you have an abstract class and an abstract method, and you find the inherited methods want to have a different parameter from the abstract method. Pass the parameters in from the constructor, not the method itself.
  • Interface segregation
    • Say you want two sets of interfaces, some subclasses only have one, while others have both. Instead put them all in one abstract class, you can 
      • Put one set into one abstract class, inherit it and add the second set
      • Create two abstract classes for two sets of interfaces separately, and combine them by composition in the subclass.
  • Dependency inversion
    • Instead of depending/coupling on a concrete class, you create an abstract class and depend on it, and make your original concrete class inherit from the abstract class.
    • This will make adding new inheritance to the abstract class easier.

Design patterns

  • Creational
    • Singleton: only one instance; global access
      • For python, defining it as a module is the best way to do it.
    • Factory: 
      • When
        • You want to create a group of objects together 
          • if there is just one object you want to create, just construct it.
        • There are only a few valid combinations of objects
          • If any combination is allowed, you’d better use bridge pattern
      • Factory is not the owner of the created objects
      • Abstract factory and then derive it for every valide combination
  • Behavioral
    • Strategy: A function/algorithm can be done in different ways. 
      • put the different strategies into classes/functions, and pass it in instead of passing a string/enum around and do different stuff based on it.
      • If different strategies have different parameter variables. Make each strategy a class, and pass parameters to the constructor (Liskov substitution).
    • Template: Base class defines the skeleton of the algorithm. Derived class can override some of the steps in the algorithm.
      • It is very similar to the strategy pattern if you implement different strategies with classes. There are two small differences:
        • Template has multiple steps, strategy usually has just one step. That is why you can use functions for the strategy pattern. 
        • For the strategy pattern, the strategy is passed to a function to call. In the template pattern, you implement the skeleton function in the base class and inherit it to all strategies.
    • Observer / Publisher-Subscriber / Callback / Event system: one-to-many dependency at runtime, one object changes its state; notifies many dependents. One trigger causes many listeners to do some stuff. 
      • Note that when I say one, it does not mean there is really just one publisher/event. There could be a few different publisher/events, and they all have the same subscribers/listeners. 
      • Instead of writing code that directly manipulates the subscribers/listeners at the publisher/event end, you write the code at the subscribers/listeners’ end and hand the function/callback over (subscribe) to the publisher/event to call.
  • Structural
    • Adaptor / Wrapper: convert one interface into another interface
      • Composition instead of inheritance
      • Delegate so that it works under the new interface.
    • Bridge: decouple two things by using composition.
      • Shape and color class, Shape contains a color.
      • Difference with Strategy:
        • Strategy is passed into a function
        • Bridge is passed into a class as a composition. This is why strategy is a behavior and bridge is a structural

Dependency Injection vs dependency inversion:

  • Dependency of two classes (strong to weak)
    • Inheritance
    • Composition: has as member variable
    • Passed in as parameter:
  • Dependency injection is a design pattern
    • Create outside and pass it in(to the constructor or the method).
    • Separate creating and using
  • Dependency inversion is a design principle in SOLID:
    • In addition to passing in, it also needs an abstract class to reduce the dependency
    • This will make adding inheritance easier.

Code smells:

  • Use string to represent a type -> enum
  • Duplicate code -> extract out as a function or class
  • Not using built-in functions (e.g. list comprehension)
  • Vague variable / function name (e.g. no unit)
  • Isinstance -> inheritance
  • Boolean flag to do two different things -> break into two functions
  • Catch and ignore all exception
  • Not using custom exceptions -> custom exceptions can carry custom fields to provide more info.

Date: 2021-11-24 Wed 00:00