Skip to content

Commit ca89195

Browse files
authored
Merge pull request #377 from realpython/solid-principles-python
Sample code for the SOLID principles article
2 parents b6e824c + 73a3814 commit ca89195

File tree

6 files changed

+251
-0
lines changed

6 files changed

+251
-0
lines changed

solid-principles-python/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# SOLID Principles: Improve Object-Oriented Design in Python
2+
3+
This folder provides the code examples for the Real Python tutorial [SOLID Principles: Improve Object-Oriented Design in Python](https://realpython.com/solid-principles-python/).

solid-principles-python/app_dip.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
from abc import ABC, abstractmethod
2+
3+
# Bad example
4+
# class FrontEnd:
5+
# def __init__(self, back_end):
6+
# self.back_end = back_end
7+
8+
# def display_data(self):
9+
# data = self.back_end.get_data_from_database()
10+
# print("Display data:", data)
11+
12+
13+
# class BackEnd:
14+
# def get_data_from_database(self):
15+
# """Return data from the database."""
16+
# return "Data from the database"
17+
18+
19+
# Good example
20+
class FrontEnd:
21+
def __init__(self, data_source):
22+
self.data_source = data_source
23+
24+
def display_data(self):
25+
data = self.data_source.get_data()
26+
print("Display data:", data)
27+
28+
29+
class DataSource(ABC):
30+
@abstractmethod
31+
def get_data(self):
32+
pass
33+
34+
35+
class Database(DataSource):
36+
def get_data(self):
37+
return "Data from the database"
38+
39+
40+
class API(DataSource):
41+
def get_data(self):
42+
return "Data from the API"
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
from pathlib import Path
2+
from zipfile import ZipFile
3+
4+
# Bad example
5+
# class FileManager:
6+
# def __init__(self, filename):
7+
# self.filename = Path(filename)
8+
9+
# def read(self):
10+
# with self.filename.open(mode="r") as file:
11+
# data = file.read()
12+
# return data
13+
14+
# def write(self, data):
15+
# with self.filename.open(mode="w") as file:
16+
# file.write(data)
17+
18+
# def compress(self):
19+
# with ZipFile(self.filename.stem + ".zip", mode="w") as archive:
20+
# archive.write(self.filename)
21+
22+
# def decompress(self, archive_name):
23+
# with ZipFile(archive_name, mode="r") as archive:
24+
# archive.extractall()
25+
26+
27+
# Good example
28+
class FileManager:
29+
def __init__(self, filename):
30+
self.filename = Path(filename)
31+
32+
def read(self):
33+
with self.filename.open(mode="r") as file:
34+
data = file.read()
35+
return data
36+
37+
def write(self, data):
38+
with self.filename.open(mode="w") as file:
39+
file.write(data)
40+
41+
42+
class ZipFileManager:
43+
def __init__(self, filename):
44+
self.filename = Path(filename)
45+
46+
def compress(self):
47+
with ZipFile(self.filename.stem + ".zip", mode="w") as archive:
48+
archive.write(self.filename)
49+
50+
def decompress(self, archive_name):
51+
with ZipFile(archive_name, mode="r") as archive:
52+
archive.extractall()
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
from abc import ABC, abstractmethod
2+
3+
# Bad example
4+
# class Printer(ABC):
5+
# @abstractmethod
6+
# def print(self, document):
7+
# pass
8+
9+
# @abstractmethod
10+
# def fax(self, document):
11+
# pass
12+
13+
# @abstractmethod
14+
# def scan(self, document):
15+
# pass
16+
17+
18+
# class OldPrinter(Printer):
19+
# def print(self, document):
20+
# print(f"Printing {document} in black and white...")
21+
22+
# def fax(self, document):
23+
# raise NotImplementedError("Fax functionality not supported")
24+
25+
# def scan(self, document):
26+
# raise NotImplementedError("Scan functionality not supported")
27+
28+
29+
# class ModernPrinter(Printer):
30+
# def print(self, document):
31+
# print(f"Printing {document} in color...")
32+
33+
# def fax(self, document):
34+
# print(f"Faxing {document}...")
35+
36+
# def scan(self, document):
37+
# print(f"Scanning {document}...")
38+
39+
40+
# Good example
41+
class Printer(ABC):
42+
@abstractmethod
43+
def print(self, document):
44+
pass
45+
46+
47+
class Fax(ABC):
48+
@abstractmethod
49+
def fax(self, document):
50+
pass
51+
52+
53+
class Scanner(ABC):
54+
@abstractmethod
55+
def scan(self, document):
56+
pass
57+
58+
59+
class OldPrinter(Printer):
60+
def print(self, document):
61+
print(f"Printing {document} in black and white...")
62+
63+
64+
class NewPrinter(Printer, Fax, Scanner):
65+
def print(self, document):
66+
print(f"Printing {document} in color...")
67+
68+
def fax(self, document):
69+
print(f"Faxing {document}...")
70+
71+
def scan(self, document):
72+
print(f"Scanning {document}...")
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
class Rectangle:
2+
def __init__(self, width, height):
3+
self.width = width
4+
self.height = height
5+
6+
def area(self):
7+
return self.width * self.height
8+
9+
def perimeter(self):
10+
return 2 * (self.width + self.height)
11+
12+
13+
class Square(Rectangle):
14+
def __init__(self, side):
15+
super().__init__(side, side)
16+
self.side = side
17+
18+
@property
19+
def side(self):
20+
return self._side
21+
22+
@side.setter
23+
def side(self, value):
24+
self._side = value
25+
self.width = value
26+
self.height = value
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
from abc import ABC, abstractmethod
2+
from math import pi
3+
4+
# Bad example
5+
# class Shape:
6+
# def __init__(self, shape_type, **kwargs):
7+
# self.shape_type = shape_type
8+
# if self.shape_type == "rectangle":
9+
# self.width = kwargs["width"]
10+
# self.height = kwargs["height"]
11+
# elif self.shape_type == "circle":
12+
# self.radius = kwargs["radius"]
13+
14+
# def calculate_area(self):
15+
# if self.shape_type == "rectangle":
16+
# return self.width * self.height
17+
# elif self.shape_type == "circle":
18+
# return pi * self.radius**2
19+
20+
21+
# Good example
22+
class Shape(ABC):
23+
def __init__(self, shape_type):
24+
self.shape_type = shape_type
25+
26+
@abstractmethod
27+
def calculate_area(self):
28+
pass
29+
30+
31+
class Circle(Shape):
32+
def __init__(self, radius):
33+
super().__init__("circle")
34+
self.radius = radius
35+
36+
def calculate_area(self):
37+
return pi * self.radius**2
38+
39+
40+
class Rectangle(Shape):
41+
def __init__(self, width, height):
42+
super().__init__("rectangle")
43+
self.width = width
44+
self.height = height
45+
46+
def calculate_area(self):
47+
return self.width * self.height
48+
49+
50+
class Square(Shape):
51+
def __init__(self, side):
52+
super().__init__("square")
53+
self.side = side
54+
55+
def calculate_area(self):
56+
return self.side**2

0 commit comments

Comments
 (0)