This work consists of two tasks focused on applying key programming principles in Python.
- Task 1 – Factory Pattern: Refactor a basic vehicle creation system using the Factory design pattern. The goal is to support region-specific specifications (e.g., US Spec, EU Spec) without modifying the core vehicle classes.
- Task 2 – SOLID Principles: Redesign a simple command-line library management program to follow SOLID principles. The task involves introducing appropriate abstractions and interfaces to make the system more modular, extensible, and maintainable.
This guide will help you set up the development environment and run tasks from the project.
Before starting, ensure that you have the following installed:
- Python 3.10+ (tested with 3.12.3)
- Poetry
- (Optional) Git for cloning the repo
- (Optional) VS Code with the Python and Black extension
-
Clone the Repository
If you haven't cloned the project yet, you can do so using:
git clone https://github.com/oleksandr-romashko/goit-pythonweb-hw-01.git cd goit-pythonweb-hw-01
or download the ZIP archive from GitHub Repository and extract it.
-
Install Dependencies
poetry install
-
Install Git Hooks with Pre-commit
Run this to enable automatic formatting and checks on each commit:
poetry run pre-commit install
This enables checks like:
- Auto-formatting with Black
- Docstring-first enforcement
- Trailing whitespace & EOL fixer
Task 1 (Factory Pattern)
poetry run python src/goit_pythonweb_hw_01/task_1/main.py
Task 2 (SOLID Principles)
poetry run python src/goit_pythonweb_hw_01/task_2/main.py
VS Code debug configurations are preconfigured in the .vscode/ directory. If you use VS Code, open the project and choose the appropriate task to run (Run task 1
or Run task 2
) from the debug panel.
You can customize these further as needed.
Both tasks require:
- The use of type hints for type safety.
- INFO-level logging instead of print statements.
- Code formatting with Black.
The following code demonstrates a simple system for creating vehicles. We have two classes: Car
and Motorcycle
. Each class has a start_engine()
method that simulates starting the engine of the respective vehicle. Currently, to create a new vehicle, we simply instantiate the corresponding class with a specified make
and model
.
class Car:
def __init__(self, make, model):
self.make = make
self.model = model
def start_engine(self):
print(f"{self.make} {self.model}: Engine started")
class Motorcycle:
def __init__(self, make, model):
self.make = make
self.model = model
def start_engine(self):
print(f"{self.make} {self.model}: Engine started")
# Usage
vehicle1 = Car("Toyota", "Corolla")
vehicle1.start_engine()
vehicle2 = Motorcycle("Harley-Davidson", "Sportster")
vehicle2.start_engine()
Next, we need to create vehicles that account for regional specifications — for example, US Spec and EU Spec.
The task is to implement the Factory pattern
, which allows creating vehicles with different regional specifications without modifying the core vehicle classes.
- Create an abstract base class
Vehicle
with astart_engine()
method. - Update the
Car
andMotorcycle
classes to inherit from Vehicle. - Create an abstract class
VehicleFactory
with methodscreate_car()
andcreate_motorcycle()
. - Implement two factory classes:
USVehicleFactory
andEUVehicleFactory
. These factories should create vehicles labeled with the appropriate regional spec, for example,Ford Mustang (US Spec)
for the US. - Refactor the initial code to use these factories for creating vehicles.
- Code that makes it easy to create vehicles for different regions using the appropriate factory.
There is a simplified program for managing a library of books. The program allows adding new books, removing books, and displaying all books in the library. The user can interact with the program via command-line using commands: add
, remove
, show
, and exit
.
class Library:
def __init__(self):
self.books = []
def add_book(self, title, author, year):
book = {
"title": title,
"author": author,
"year": year
}
self.books.append(book)
def remove_book(self, title):
for book in self.books:
if book["title"] == title:
self.books.remove(book)
break
def show_books(self):
for book in self.books:
print(f'Title: {book["title"]}, Author: {book["author"]}, Year: {book["year"]}')
def main():
library = Library()
while True:
command = input("Enter command (add, remove, show, exit): ").strip().lower()
if command == "add":
title = input("Enter book title: ").strip()
author = input("Enter book author: ").strip()
year = input("Enter book year: ").strip()
library.add_book(title, author, year)
elif command == "remove":
title = input("Enter book title to remove: ").strip()
library.remove_book(title)
elif command == "show":
library.show_books()
elif command == "exit":
break
else:
print("Invalid command. Please try again.")
if __name__ == "__main__":
main()
The task is to rewrite this code following the SOLID principles.
- Single Responsibility Principle (SRP): Create a
Book
class that is responsible for storing book information. - Open/Closed Principle (OCP): Design the
Library
class so that it can be extended with new functionality without modifying its existing code. - Liskov Substitution Principle (LSP): Ensure that any class inheriting from the
LibraryInterface
can replace theLibrary
class without breaking the program. - Interface Segregation Principle (ISP): Use a
LibraryInterface
to clearly specify the methods required to work with the library. - Dependency Inversion Principle (DIP): High-level classes like
LibraryManager
should depend on abstractions (interfaces), not concrete implementations.
from abc import ABC, abstractmethod
class Book:
pass
class LibraryInterface(ABC):
pass
class Library(LibraryInterface):
pass
class LibraryManager:
pass
def main():
library = Library()
manager = LibraryManager(library)
while True:
command = input("Enter command (add, remove, show, exit): ").strip().lower()
match command:
case "add":
title = input("Enter book title: ").strip()
author = input("Enter book author: ").strip()
year = input("Enter book year: ").strip()
manager.add_book(title, author, year)
case "remove":
title = input("Enter book title to remove: ").strip()
manager.remove_book(title)
case "show":
manager.show_books()
case "exit":
break
case _:
print("Invalid command. Please try again.")
if __name__ == "__main__":
main()
Solution for this task is located in the following file:
- src/goit_pythonweb_hw_01/task_1/main.py - developed solution for task 1.
Application screenshot:
Solution for this task is located in the following file: src/goit_pythonweb_hw_01/task_2/main.py - developed solution for task 2.
Application screenshot: