The document discusses reviewing object-oriented design patterns. It begins with definitions of design patterns and their history. It then covers the core tenets of OOP like encapsulation, abstraction, inheritance, polymorphism. Specific patterns like singleton, command, and dependency injection are explained with examples. The document stresses principles for good design like avoiding duplication, loose coupling, single responsibility.
2. Introduction
Definition
In software engineering, a software design pattern is a general reusable
solution to a commonly occurring problem within a given context in
software design.
It is not a finished design that can be transformed directly into source
code.
The implementation approaches tend to be slightly different.
3. Why spend the time?
- From framework user, to framework creator in a software engineer
career
- Where to direct our attention: brush up on foundations, not
frameworks
- … And time is finite. Why spend too much time on a problem that has
been solved already?
4. History
1977: Christopher Alexander introduces the idea of patterns: successful
solutions to problems. (Pattern Language)
1987: Ward Cunningham and Kent Beck leverage Alexander’s idea in the
context of an OO language.
1987: Eric Gamma’s dissertation on importance of patterns and how to
capture them.
1994: The book =>
6. Core principles of OOP:
Encapsulation
const account = { balance: 1000, owner: 13466 }
example: a bank account object
7. Core principles of OOP:
Encapsulation
const account = { balance: 1000, owner: 13466 }
-> Encapsulate the properties
! Manipulating properties directly
example: a bank account object
8. Core principles of OOP:
Encapsulation
class Account {
private balance: number
private owner: Customer
public withdraw(amount: number) { // can validate }
public deposit(amount: number) {}
}
Its public API
const account = { balance: 1000, owner: 13466 }
The object’s internals, what you hide / protect
11. class EmailService {
private connectToRemoteServer(){}
private validateTokenLength(){}
private disconnectFromRemoteServer(){}
public sendEmail(){ // leverages all the above }
}
Core principles of OOP:
Abstractions
Things that might be extended/removed
be quite complex...
A neat public API
12. Core principles of OOP:
Inheritance
class Rectangle {
fillShape(){ // }
}
example: an design app
class Circle {
fillShape(){ // }
}
13. Core principles of OOP:
Inheritance
class Rectangle {
fillShape(){ // }
}
example: an design app
class Circle {
fillShape(){ // }
}
! Make your code more DRY by avoiding repetitions
-> Add logic to a common ancestor
14. Core principles of OOP:
Inheritance
example: an design app
class Shape {
public fill(colour: string){ //behaviour }
}
class Circle extends Shape {}
const circle = new Circle()
circle.fill(”#FF0000”)
A way to write logic once and update it
downward
15. Quick recall: the concept of interfaces
An interface is a reference type in OOP.
A reference Type is a “specification type”. We can’t explicitly use it, but it is used
internally by the language.
It enforces a contract that the derived classes will have to obey
Interfaces are an “ideal”
16. Core principles of OOP:
Polymorphism( “Can take many forms” )
example: an animal game for children
interface IAnimal {
makeSound(): string
}
abstract class AbstractAnimal {
abstract makeSound(): string
}
the prefix I is a Convention
17. Core principles of OOP:
Polymorphism( “Can take many forms” )
example: an animal game for children
class Cat implements IAnimal {
public makeSound():string { return “Miaou” }
}
class Cat extend AbstractAnimal {
public makeSound():string { return “Miaou” }
}
18. Your “API” to other classes
Quick recall: access modifiers
22. Fundamental principles
Single responsibility principle, see your code as modular black boxes
Easier to explain
Less surprising
Class is much less likely to
change often
The name should be able to
tell you what it does
23. Open-Close principle
Open to extension, closed for modification
class CoffeeMaker {
final grindCoffee(): void
final runJob(program: string): void
}
class FancyCoffeMaker extends CoffeeMaker {
runJob(program: string, callback: Function): void
}
Inheritance bring tight coupling
New features should not change the
base class
! Open
!! New feature !!
Don’t change
24. Dependency Injection
High level modules should not depend upon low level modules.
class EmployeSearchService {
private employeesTable = new DBConnection(3306, “somewhere.”, “admin”, “admin123”, “employees”);
public getById(id: string) {
return this.employeesTable.findbyId(id);
}
}
25. Dependency Injection
High level modules should not depend upon low level modules.
class EmployeSearchService {
private employeesTable = new DBConnection(3306, “somewhere.”, “admin”, “admin123”, “employees”);
public getById(id: string) {
return this.employeesTable.findbyId(id);
}
}
!! New feature !! Extended search
26. Dependency Injection
High level modules should not depend upon low level modules.
class EmployeSearchService {
private employeeDataStore: EmployeeDataStore
constructor(dataStore: EmployeeDataStore) {
this.employeeDataStore = dataStore
}
public getById(id: string) {
return this.employeeDataStore.findbyId(id);
}
}
new EmployeeSearchService(new EmployeeNapkins())
new EmployeeSearchService(new ExmployeeExcel())
new EmployeeSearchService(new EmployeeCSV())
Give the stores a common interface
Make your code more flexible
27. Fundamental principles
- LSP: Liskov Substitution Principle.
Derived classes must be substitutable for their base classes
- ISP: Interface Segregation Principle
Client should not be forced to depend upon interfaces they do not use
29. Recall: UML
A visual, language agnostic way to map OOP: Unified Modelling Language
Access:
+ public - private # protected
Properties
class name
Methods
30. The Five Families of Design Patterns
Creational
Structural Foundational
Concurrency
Behavioural
Modern days design pattern families
31. The Three Main Families of Design Patterns
Behavioural
Structural
Creational
According to the Gang of Four
23 design patterns
32. Behavioural Patterns
Used to responsibilities between objects and manage algorithms
● Command
● Observer
● Strategy
● State
● Visitor
● Memento
● Mediator
● Iterator
33. Structural Patterns
Used to form large structures between many disparate objects
● Adapter
● Proxy
● Decorator
● Facade
● Aggregate
● Bridge
34. Creational Patterns
Used to construct object so that they can be decoupled from their
implementing system
● Singleton
● Factory Method Pattern
● Abstract Factory
● Builder
● Prototype
35. Behavioural Patterns
Command pattern: An object oriented replacement for callbacks
“Encapsulate a request as an object, thereby letting users parameterize
clients with different requests, queue or log request, and support
undoable operations”
36. Behavioural Patterns
Command pattern: An object oriented replacement for callbacks
example: configuring input on a game controller
Y
X B
A
Fire Gun
Jump
Duck
Swap weapon
You want to:
> Map inputs to actions in an OO way
37. Behavioural Patterns
Command pattern: An object oriented replacement for callbacks
example: game controller commands
class InputHandler {
static handleInput() {
if(isPressed(BUTTON_X) jump()
else if(isPressed(BUTTON_Y) fireGun()
else if(isPressed(BUTTON_A) swapWeapon()
else if(isPressed(BUTTON_B) duck()
}
}
38. Behavioural Patterns
Command pattern: An object oriented replacement for callbacks
example: game controller commands
Users usually like to map
their own controls to the
inputs
class InputHandler {
static handleInput() {
if(isPressed(BUTTON_X) jump()
else if(isPressed(BUTTON_Y) fireGun()
else if(isPressed(BUTTON_A) swapWeapon()
else if(isPressed(BUTTON_B) duck()
}
}
39. Behavioural Patterns
Command pattern: An object oriented replacement for callbacks
example: game controller commands
> You want to make the direct calls to jump() and fireGun() into something that we can swap
in and out.
40. Behavioural Patterns
Command pattern: An object oriented replacement for callbacks
example: game controller commands
interface ICommand {
execute():void
}
class JumpCommand implements ICommand{
execute(): void { jump(); }
}
class DuckCommand implements ICommand{
execute():void { duck(); }
}
41. class InputHandler {
private buttonA: Command;
private buttonB: Command;
private buttonX: Command;
private buttonY: Command;
public setButtonA(command: Command): void {this.buttonA = command;}
public setButtonB(command: Command): void {this.buttonB = command;}
public setButtonX(command: Command): void {this.buttonX = command;}
public setButtonY(command: Command): void {this.buttonY = command;}
}
Behavioural Patterns
Command pattern: An object oriented replacement for callbacks
42. class InputHandler {
...
public inputHandler():void {
if(isPressed(BUTTON_X) return buttonX.execute()
else if(isPressed(BUTTON_Y) return buttonY.execute()
else if(isPressed(BUTTON_A) return buttonA.execute()
else if(isPressed(BUTTON_B) return buttonB.execute()
return null
}
}
Behavioural Patterns
Command pattern: An object oriented replacement for callbacks
44. Behavioural Patterns
Command pattern: An object oriented replacement for callbacks
Bonus: Undoing things
interface ICommand {
execute():void
undo():void
}
45. Behavioural Patterns
Command pattern: An object oriented replacement for callbacks
class MoveUnit {
private x: number;
private y: number;
private xBefore: number;
private yBefore: number;
private unit: ArmyUnit;
constructor(unit: ArmyUnit, x: number, y: number){
this.x = x;
this.y = y;
}
public execute(){
this.xBefore = this.x;
this.yBefore = this.y;
this.unit.moveTo(this.x, this.y)
}
public undo(){
this.unit.moveTo(this.xBefore, this.yBefore)
}
}
CMD CMD CMD CMD CMD
Older Newer
An undo stack
example: a turn by turn strategy game
Undo Redo
46. Creational Patterns
Singleton pattern: There is power in ONE
“The singleton pattern ensures that only one object of a particular class
is ever created. All further references to objects of the singleton class
refer to the same underlying instance.’
47. Creational Patterns
Singleton pattern: There is power in ONE
example: a logger
> You want to create a logger that adds log lines to files from different locations and
potentially at the same time
> You logs have to maintain the order of the events
> You need to avoid “file already in use” and other lock related issues
48. Creational Patterns
Singleton pattern: There is power in ONE
example: a logger
Not just for a logger, but for most of the xManager, xEngine, xSystem classes of
objects.
Like
- DBConnectionManager
- FilesystemManager
- ...
49. Creational Patterns
Singleton pattern: There is power in ONE
A solution is provided by the singleton pattern:
Enforcing a single global instance that wraps resources you want to protect
50. Creational Patterns
Singleton pattern: There is power in ONE
example: a logger
class Logger {
private static _instance: MyClass;
private constructor() { // initialisation... }
public static getInstance() {
return this._instance || (this._instance = new this());
}
}
const logger = Logger.getInstance();
51. Creational Patterns
Singleton pattern: There is power is ONE
Global state
Not garbage
collected
There for the
entire life of
the program
*
*Once lazy loaded
Theoretically
accessible from
everywhere
52. Resources
Another take: The anti patterns http://www.laputan.org/mud/
Build your UML graphs with Lucid Charts
https://www.lucidchart.com/pages/examples/uml_diagram_tool
Readings
Tools