1.1 SOLID פתרון
תרגיל 1 - פתרון¶
עקרון מופר: Single Responsibility Principle
למחלקה UserManager יש ארבע אחריויות שונות:
1. שמירה למסד נתונים
2. הצפנת סיסמאות
3. שליחת מיילים
4. לוגינג
תיקון:
import hashlib
import sqlite3
import smtplib
class PasswordHasher:
def hash(self, password: str) -> str:
return hashlib.sha256(password.encode()).hexdigest()
class UserRepository:
def save(self, username: str, hashed_password: str):
db = sqlite3.connect("app.db")
db.execute("INSERT INTO users VALUES (?, ?)", (username, hashed_password))
db.commit()
class WelcomeEmailService:
def send(self, username: str):
server = smtplib.SMTP("smtp.gmail.com")
server.sendmail("no-reply@app.com", username, "ברוך הבא!")
class UserManager:
def __init__(
self,
hasher: PasswordHasher,
repository: UserRepository,
email_service: WelcomeEmailService
):
self.hasher = hasher
self.repository = repository
self.email_service = email_service
def create_user(self, username: str, password: str):
hashed = self.hasher.hash(password)
self.repository.save(username, hashed)
self.email_service.send(username)
print(f"משתמש {username} נוצר בהצלחה")
תרגיל 2 - פתרון¶
עקרונות מופרים: Interface Segregation + Liskov Substitution
SimpleReport מאולצת לממש מתודות שאינה צריכה, וזורקת NotImplementedError בחלקן - הפרת LSP.
תיקון:
from abc import ABC, abstractmethod
class PDFGenerator(ABC):
@abstractmethod
def generate_pdf(self) -> str: pass
class ExcelGenerator(ABC):
@abstractmethod
def generate_excel(self) -> str: pass
class EmailSender(ABC):
@abstractmethod
def send_by_email(self, email: str): pass
class DiskSaver(ABC):
@abstractmethod
def save_to_disk(self, path: str): pass
class SimpleReport(PDFGenerator, DiskSaver):
def generate_pdf(self) -> str:
return "PDF report"
def save_to_disk(self, path: str):
with open(path, "w") as f:
f.write(self.generate_pdf())
class FullReport(PDFGenerator, ExcelGenerator, EmailSender, DiskSaver):
def generate_pdf(self) -> str:
return "PDF report"
def generate_excel(self) -> str:
return "Excel report"
def send_by_email(self, email: str):
print(f"שולח ל-{email}")
def save_to_disk(self, path: str):
with open(path, "w") as f:
f.write(self.generate_pdf())
תרגיל 3 - פתרון¶
עקרון מופר: Open/Closed Principle
בכל פעם שמוסיפים שיטת תשלום חדשה, צריך לשנות את PaymentProcessor.
תיקון:
from abc import ABC, abstractmethod
class PaymentMethod(ABC):
@abstractmethod
def process(self, amount: float):
pass
class CreditCardPayment(PaymentMethod):
def process(self, amount: float):
print(f"מעבד כרטיס אשראי: {amount}")
class PayPalPayment(PaymentMethod):
def process(self, amount: float):
print(f"מעבד PayPal: {amount}")
class BitcoinPayment(PaymentMethod):
def process(self, amount: float):
print(f"מעבד Bitcoin: {amount}")
class PaymentProcessor:
def process(self, method: PaymentMethod, amount: float):
method.process(amount)
# שימוש
processor = PaymentProcessor()
processor.process(CreditCardPayment(), 100.0)
processor.process(BitcoinPayment(), 50.0)
TaskFlow - פתרון¶
הפרה 1 - SRP: ה-route של notify_assignee מבצע גם שליפת נתונים מה-DB וגם שליחת מייל. שני דברים שונים.
הפרה 2 - DIP: הפונקציות תלויות ישירות ב-sqlite3.connect - קוד ברמה גבוהה (logic) תלוי ישירות ביישום ברמה נמוכה (database).
הפרה 3 - SRP + אבטחה: hashlib.md5 בתוך route ה-register - לוגיקת הצפנה מעורבת בתוך שכבת ה-HTTP. בנוסף, MD5 לא בטוח לסיסמאות.