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)