לדלג לתוכן

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 לא בטוח לסיסמאות.