Gergő Pintér, PhD
gergo.pinter@uni-corvinus.hu
creational
structural
behavioral
read about the design patterns in details, for example at refactoring.guru
OO pattern | FP pattern |
---|---|
factory pattern | function |
strategy pattern | function |
decorator pattern | function |
visitor pattern | function |
… | … |
Peter Norvig demonstrated that 16 out of the 23 patterns are simplified or eliminated by language features in Lisp or Dylan (1998) [3]
more about it from Scott Wlaschin [4]
Always implement things when you actually need them, never when you just foresee that you need them.
extreme programming
source Wikipedia [5]
SOLID is a mnemonic acronym for five design principles intended to make object-oriented designs more understandable, flexible, and maintainable [6]
a class should do one thing and therefore it should have only a single reason to change
Unix philosophy
Make each program do one thing well. To do a new job, build afresh rather than complicate old programs by adding new “features”.
advantages
classes should be open for extension and closed to modification
class Shape:
pass
class Square(Shape):
def __init__(self, width: float):
self.width = width
class Circle(Shape):
def __init__(self, radius: float):
self.radius = radius
class AreaCalculator:
def sum(self, shapes: list[Shape]) -> float:
result = 0
for shape in shapes:
if isinstance(shape, Square):
result += shape.width**2
elif isinstance(shape, Circle):
result += shape.radius**2 * math.pi
return round(result, 2)
example based on [7]
class Shape:
pass
class AreaInterface:
def area(shape: Shape) -> float:
pass
class Square(Shape, AreaInterface):
def __init__(self, width: float):
self.width = width
def area(self) -> float:
return self.width**2
class Circle(Shape, AreaInterface):
def __init__(self, radius: float):
self.radius = radius
def area(self) -> float:
return round(self.radius**2 * math.pi, 2)
class AreaCalculator:
def sum(self, shapes: list[Shape]) -> float:
return sum([i.area() for i in shapes])
example based on [7]
if class A is a subtype of class B, B should be able to replaced with A without disrupting the behavior of the program [8]
class Rectangle:
def __init__(self, width: int, height: int):
self.__width = width
self.__height = height
def setWidth(self, width: int):
self.__width = width
def setHeight(self, height: int):
self.__height = height
def getWidth(self):
return self.__width
def getHeight(self):
return self.__height
def getArea(self):
return self.__width * self.__height
code is based on [11]
def getAreaTest(r: Rectangle):
width = r.getWidth() # width is 2
r.setHeight(10)
return f"Expected area of {width * 10}, got {r.getArea()}"
>>> r = Rectangle(2, 3)
>>> print(r.getArea())
6
>>> s = Square(2)
>>> print(s.getArea())
4
>>> print(getAreaTest(r)) # rectangle
Expected area of 20, got 20
>>> print(getAreaTest(s)) # square
Expected area of 20, got 100
this example violates the Liskov substitution principle
code is based on [11]
states that many client-specific interfaces are better than one general-purpose interface. Clients should not be forced to implement a function they do no need.
example based on [7]
Dependency inversion principle says that modules should depend upon interfaces or abstract classes, not concrete classes. It’s an inversion because implementations depend upon abstractions and not the other way round. [8]
increases reusability
don’t call us, we’ll call you
based on [12]
Object-oriented design (OOD) is the process of planning a system of interacting objects to solve a software problem [13].
control flow? structure?
models
delivery guaranties
based on [15] and [16]
number of layers in a layered architecture is not set to a specific number
advantages
disadvantages
based on [17]
based on [18]
based on [14]
based on [14]
can be extend without changing the business logic
it is very similar to the onion and (the clean architecture [20])
based on [14]
ASP.NET, Django (Python), Ruby on Rails, Laravel (PHP)
Windows Forms, Java Swing
WPF, AngularJS
figures based on [22]
other alternatives: Alternatives To MVC - by Anthony Ferrara
as a user I want to see my activity to see my progress
send everything to the UI
in this case the UI has to calculate the daily activity
send only the aggregated data
data collector still has the whole user data but that aligns with its purpose
data aggregator calculates everything and the UI only displays it
UI might be on a client
different code base, different language
make the database aggregate the data
on the other hand, most of these are present in all the three architectures!
in each architecture decision record, write these sections:
# Title
## Status
What is the status, such as proposed, accepted, rejected, deprecated, superseded, etc.?
## Context
What is the issue that we're seeing that is motivating this decision or change?
## Decision
What is the change that we're proposing and/or doing?
## Consequences
What becomes easier or more difficult to do because of this change?
ADR template by Michael Nygard from Documenting architecture decisions
based on Why Write ADRs by Eli Perkins