לדלג לתוכן

2.2 תבניות מבנה פתרון

תרגיל 1 - Adapter - פתרון

class FileStorageAdapter(AttachmentStore):
    FOLDER = "attachments"

    def __init__(self, storage: FileStorage):
        self._storage = storage

    def save(self, content: bytes, name: str) -> str:
        # מתאם save -> upload_file
        url = self._storage.upload_file(content, name, self.FOLDER)
        return url  # ה-URL משמש כ-attachment_id

    def delete(self, attachment_id: str) -> None:
        # מתאם delete -> remove_file
        success = self._storage.remove_file(attachment_id)
        if not success:
            raise RuntimeError(f"לא הצליח למחוק: {attachment_id}")

    def get(self, attachment_id: str) -> bytes:
        # מתאם get -> retrieve_file
        return self._storage.retrieve_file(attachment_id)


# שימוש
storage = FileStorage()
adapter = FileStorageAdapter(storage)

attachment_id = adapter.save(b"file content", "report.pdf")
content = adapter.get(attachment_id)
adapter.delete(attachment_id)

תרגיל 2 - Decorator - פתרון

import time


class TimedTaskService:
    def __init__(self, wrapped: TaskService):
        self._wrapped = wrapped

    def get_task(self, task_id: int) -> dict:
        start = time.perf_counter()
        result = self._wrapped.get_task(task_id)
        elapsed = time.perf_counter() - start
        print(f"[TIMER] get_task לקח {elapsed:.3f}s")
        return result

    def update_task(self, task_id: int, title: str) -> dict:
        start = time.perf_counter()
        result = self._wrapped.update_task(task_id, title)
        elapsed = time.perf_counter() - start
        print(f"[TIMER] update_task לקח {elapsed:.3f}s")
        return result


class RetryTaskService:
    MAX_RETRIES = 3

    def __init__(self, wrapped: TaskService):
        self._wrapped = wrapped

    def get_task(self, task_id: int) -> dict:
        return self._wrapped.get_task(task_id)

    def update_task(self, task_id: int, title: str) -> dict:
        last_error = None
        for attempt in range(1, self.MAX_RETRIES + 1):
            try:
                return self._wrapped.update_task(task_id, title)
            except Exception as e:
                last_error = e
                print(f"[RETRY] ניסיון {attempt} נכשל: {e}")
        raise last_error


# שרשור decorators
base = TaskService()
with_retry = RetryTaskService(base)
with_timing_and_retry = TimedTaskService(with_retry)

result = with_timing_and_retry.update_task(1, "כותרת חדשה")

תרגיל 3 - Facade - פתרון

class SimpleTaskRepo:
    def __init__(self):
        self._tasks = []
        self._next_id = 1

    def create(self, title: str, description: str, owner_id: int) -> dict:
        task = {"id": self._next_id, "title": title, "description": description,
                "owner_id": owner_id, "status": "open", "project_id": None}
        self._tasks.append(task)
        self._next_id += 1
        return task

    def find_open_by_project(self, project_id: int) -> list[dict]:
        return [t for t in self._tasks if t["project_id"] == project_id and t["status"] == "open"]

    def mark_done(self, task_id: int):
        for task in self._tasks:
            if task["id"] == task_id:
                task["status"] = "done"


class SimpleProjectRepo:
    def __init__(self):
        self._projects = {}

    def archive(self, project_id: int):
        self._projects[project_id] = {"id": project_id, "archived": True}
        print(f"פרויקט {project_id} אורכב")


class SimpleEmailService:
    def send(self, to: str, subject: str, body: str):
        print(f"מייל ל-{to} | נושא: {subject} | גוף: {body}")


class TaskFlowFacade:
    def __init__(self):
        self._tasks = SimpleTaskRepo()
        self._projects = SimpleProjectRepo()
        self._email = SimpleEmailService()

    def create_task_with_notification(
        self,
        title: str,
        description: str,
        owner_id: int,
        owner_email: str
    ) -> dict:
        task = self._tasks.create(title, description, owner_id)
        self._email.send(
            to=owner_email,
            subject=f"משימה חדשה: {title}",
            body=f"נוצרה משימה חדשה עבורך: {title}\n{description}"
        )
        return task

    def close_project(self, project_id: int, owner_id: int):
        open_tasks = self._tasks.find_open_by_project(project_id)
        for task in open_tasks:
            self._tasks.mark_done(task["id"])
        self._projects.archive(project_id)
        print(f"פרויקט {project_id} נסגר. {len(open_tasks)} משימות סומנו כהושלם.")


# שימוש
facade = TaskFlowFacade()
task = facade.create_task_with_notification("תקן באג", "באג בlogin", 1, "dev@example.com")
facade.close_project(project_id=1, owner_id=1)