The SOLID Principles
Jan Wedekind
Thursday, Feb 22nd 2024
Thursday, Feb 22nd 2024 1/22
Motivation
Find guiding design principles to
maintain software quality over
time.
Thursday, Feb 22nd 2024 2/22
Software Rot
Symptoms of rotting software design:a
• Rigidity: software difficult (a lot of work) to change
• Fragility: changes easily break the software
• Immobility: it is easier to rewrite than reuse parts
• Viscosity: design preserving methods are harder to employ
than hacks
a Robert C. Martin: Design Principles and Design Patterns
Thursday, Feb 22nd 2024 3/22
Aims
In contrast we want to achieve the following:a
• Keep software application flexible
• Keep software application robust
• Keep software application reusable
• Keep software application developable
a Robert C. Martin: Design Principles and Design Patterns
Thursday, Feb 22nd 2024 4/22
SOLID Authors
• Author of Clean Code, Functional Design,
and more books
• Author of Design Principles and Design
Patterns paper based on his experience and
on work by Bertrand Meyer, Barbara
Liskov, and Erich Gamma et al.
Robert C. Martin
• [Link]
• Author of Working Effectively With Legacy
Code
• Summarized Robert C. Martin’s paper
using the SOLID acronym
Michael Feathers • [Link]
Thursday, Feb 22nd 2024 5/22
The SOLID Principles
1. Single responsibility
2. Open-closed
3. Liskov substitution
4. Interface segregation
5. Dependency inversion
Thursday, Feb 22nd 2024 6/22
Single Responsibility - Before
def adults_to_html(people):
result = "<ul>\n"
for person in people:
if [Link] >= 18:
result += " <li>" + [Link] + "</li>\n"
result += "</ul>"
return result
# ...
page = adults_to_html(people)
Thursday, Feb 22nd 2024 7/22
Single Responsibility - After
def select_adults(people):
return [person for person in people if [Link] >= 18]
def people_to_html(people):
result = "<ul>\n"
for person in people:
result += " <li>" + [Link] + "</li>\n"
result += "</ul>"
return result
# ...
page = people_to_html(select_adults(people))
Thursday, Feb 22nd 2024 8/22
Open-Closed - Before
def total_area(shapes):
result = 0
for shape in shapes:
match type(shape):
case Rectangle:
result += [Link] * [Link]
case Sphere:
result += [Link] * [Link] ** 2
case _:
raise f"Unsupported shape {shape}"
return result
Thursday, Feb 22nd 2024 9/22
Open-Closed - After
class Rectangle:
def area(self):
return [Link] * [Link]
class Circle:
def area(self):
return [Link] * [Link] ** 2
def total_area(shapes):
result = 0
for shape in shapes:
result += [Link]()
return result
Thursday, Feb 22nd 2024 10/22
Liskov-Substitution - Before
class Rectangle:
def __init__(self, width, height):
[Link] = width
[Link] = height
def set_width(self, width):
[Link] = width
def set_height(self, height):
[Link] = height
class Square(Rectangle):
def __init__(self, side):
super().__init__(side, side)
def set_width(self, width):
super().set_width(width)
super().set_height(width)
def set_height(self, height):
self.set_width(height)
Thursday, Feb 22nd 2024 11/22
Liskov-Substitution - After
class Shape:
pass
class Rectangle(Shape):
def __init__(self, width, height):
[Link] = width
[Link] = height
def set_width(self, width):
[Link] = width
def set_height(self, height):
[Link] = height
class Square(Shape): Barbara Liskov
def __init__(self, side):
[Link] = side
def set_side(self, side):
[Link] = side
Thursday, Feb 22nd 2024 12/22
Liskov-Substitution - Contracts
“The Liskov Substitution Principle states, among other constraints,
that a subtype is not substitutable for its super type if it
strengthens its operations’ preconditions, or weakens its operations’
postconditions”a
type precondition method postcondition
method
subtype precondition postcondition
a Baniassad: Making the Liskov Substitution Principle Happy and Sad
Thursday, Feb 22nd 2024 13/22
Interface Segregation - Before
class AccountHolder:
def __init__(self, name, age, balance):
[Link] = name
[Link] = age
[Link] = balance
def is_adult(self):
return [Link] >= 18
def deposit(self, amount):
[Link] += amount
def withdraw(self, amount):
[Link] -= amount
Thursday, Feb 22nd 2024 14/22
Interface Segregation - After
class Person:
def __init__(self, name, age):
[Link], [Link] = name, age
def is_adult(self):
return [Link] >= 18
class Account:
def __init__(self, balance):
[Link] = balance
def deposit(self, amount):
[Link] += amount
def withdraw(self, amount):
[Link] -= amount
class AccountHolder(Person):
def __init__(self, name, age, account):
super().__init__(name, age)
[Link] = account
Thursday, Feb 22nd 2024 15/22
Dependency Inversion - Before
def get_names(connection):
cursor = [Link]()
[Link]('SELECT name FROM member_table')
rows = [Link]()
names = [row[0] for row in rows]
return names
connection = [Link]('[Link]')
names_list = get_names(connection)
[Link]()
print(names_list)
Thursday, Feb 22nd 2024 16/22
Dependency Inversion - After
class Database([Link]):
@[Link]
def sql(self, query):
pass
class SQLiteDatabase(Database):
def __init__(self, db_file_name):
[Link] = [Link](db_file_name)
def __del__(self):
[Link]()
def sql(self, query):
cursor = [Link]()
[Link](query)
return [Link]()
def get_names(database):
rows = [Link]('SELECT name FROM member_table')
return [row[0] for row in rows]
Thursday, Feb 22nd 2024 17/22
Dependency Inversion - After
database = SQLiteDatabase('[Link]')
names_list = get_names(database)
print(names_list)
Thursday, Feb 22nd 2024 18/22
Aspects of a Class
The 5 aspects of the class are:a
responsibility towards parent
interface towards callers
interface towards callees
class'
purpose
responsibility towards inheritors
a Mike Lindner: The Five Principles For SOLID Software Design
Thursday, Feb 22nd 2024 19/22
The 5 Principles
The 5 corresponding principles are:a
Liskov substitution principle
dependency inversion principle
interface segregation principle
single
responsibility
principle
open-closed principle
a Mike Lindner: The Five Principles For SOLID Software Design
Thursday, Feb 22nd 2024 20/22
Arjan Egges: Uncle Bob’s SOLID Principles Made Easy
19 minutes video
Thursday, Feb 22nd 2024 21/22
Jim Weirich: The Building Blocks of Modularity
33 minutes video
Thursday, Feb 22nd 2024 22/22