The modification of the class method is not encouraged for new feature
The creation of new class method or new child class is encouraged for new feature
L - Liskov Substitution
When designing your classes , It is needed to consider that any subclass can be used as a substitute for the base class without unexpected behavior.
If you have a base class Bird with a method fly(), and you create a subclass Penguin which also has a fly() method but throws an exception because penguins can't fly, you would be violating the Liskov Substitution Principle.
I - Interface segregation
the interface should consider the necessary class method, in order to make the class more focused and maintainable
D - Dependency injection
Loose coupled between the classes
Singleton
Single instance only for a class
Global configuration ( e.g: db connection, logger) is one of the use case
class Singleton {
static instance;
/**
* The Singleton's constructor should always be private to prevent direct
* construction calls with the `new` operator.
*/
constructor() { }
/**
* The static getter that controls access to the singleton instance.
*
* This implementation allows you to extend the Singleton class while
* keeping just one instance of each subclass around.
*/
static get instance() {
if (!Singleton.instance) {
Singleton.instance = new Singleton();
}
return Singleton.instance;
}
/**
* Finally, any singleton can define some business logic, which can be
* executed on its instance.
*/
someBusinessLogic() {
// ...
}
}
/**
* The client code.
*/
function clientCode() {
const s1 = Singleton.instance;
const s2 = Singleton.instance;
if (s1 === s2) {
console.log(
'Singleton works, both variables contain the same instance.'
);
} else {
console.log('Singleton failed, variables contain different instances.');
}
}
clientCode();
Factory
Act as a agent to building the target class
Suitable when the class constructor is non-important and duplicated, the method can help to hide the detail
public abstract class Car {
public static Car factory(Color color) {
switch(color) {
case "RED":
return new RedCar();
case "BLUE":
return new BlueCar();
}
}
}
public class RedCar extends Car {...}
public class BlueCar extends Car {...}
Car redCar = car.factory("RED");
Builder
Suitable to apply when there are lots of attribute in class
Provide a convert class, to convert the format from a to b
class EuropeanSocket:
def provide_230v(self):
return "Providing 230V"
class AmericanPlug:
def connect_to_120v(self):
return "Using 120V power"
class SocketAdapter:
def __init__(self, european_socket):
self.european_socket = european_socket
def connect(self):
# Convert the power from 230V to 120V
if self.european_socket.provide_230v() == "Providing 230V":
return "Adapter converting to 120V -> " + AmericanPlug().connect_to_120v()
# Client code
european_socket = EuropeanSocket()
adapter = SocketAdapter(european_socket)
print(adapter.connect())
Strategy
Applying dependency injection, A high order function/ class to make the low-level class be interchangable
from typing import List
# Strategy interface
class SortStrategy:
def sort(self, data: List[int]) -> List[int]:
pass
# Concrete strategies
class BubbleSortStrategy(SortStrategy):
def sort(self, data: List[int]) -> List[int]:
n = len(data)
for i in range(n):
for j in range(0, n-i-1):
if data[j] > data[j+1]:
data[j], data[j+1] = data[j+1], data[j]
return data
class QuickSortStrategy(SortStrategy):
def sort(self, data: List[int]) -> List[int]:
if len(data) <= 1:
return data
pivot = data[len(data) // 2]
left = [x for x in data if x < pivot]
middle = [x for x in data if x == pivot]
right = [x for x in data if x > pivot]
return self.sort(left) + middle + self.sort(right)
# high level class , you inject the suitable low class into it
class Context:
def __init__(self, strategy: SortStrategy):
self._strategy = strategy
def set_strategy(self, strategy: SortStrategy):
self._strategy = strategy
def sort(self, data: List[int]) -> List[int]:
return self._strategy.sort(data)
# Client code
data = [5, 3, 8, 6, 2]
context = Context(BubbleSortStrategy())
print("Bubble Sort:", context.sort(data))
context.set_strategy(QuickSortStrategy())
print("Quick Sort:", context.sort(data))