Table of Contents

תבניות עיצוב מייצגות פתרונות עתיים לאתגרים חוזרים בפיתוח תוכנה.בהנדסת Python, פתרונות אלה הניתנים לחזרה לעזור למפתחים ליצור קוד אמין יותר, גמיש ומדפי, הבנה וביצוע תבניות עיצוב ביעילות יכול להפוך את האופן שבו אתה ניגש לאדריכלות תוכנה, המאפשר לך לכתוב קוד שאינו רק פונקציונלי אלא גם אלגנטי, גמיש וקל יותר לשמור על הזמן.

תבניות עיצוב משמשות אוצר מילים המאפשר למהנדסים לתקשר החלטות מבניות באופן עקבי.כאשר מפתח בכיר מציע להשתמש בדפוס ספציפי, הם מעבירים גישה ארכיטקטונית שלמה רק בכמה מילים.שפה משותפת זו מאיצה שיתוף פעולה קבוצתי ומבטיחה שכולם מבינים את המבנה הבסיסי של בסיס הקוד.

הבנת תבניות עיצוב ב- Python Context

Python היא שפת תכנות ברמה גבוהה עם הקלדה דינמי וחייב דינמי, מה שהופך אותו שפה דינמי חזק ברמה גבוהה. גמישות זו מעניקה פייתון יתרונות ייחודיים בעת יישום דפוסי עיצוב, אבל זה גם אומר כי כמה דפוסים צריך להיות מותאם כדי להתאים את המילניום של Python ואת היכולות.

כל דבר ב- Python הוא אובייקט, כולל פונקציות, שהן אובייקטים ממדרגה ראשונה.תכונה בסיסית זו משפיעה על האופן שבו תבניות עיצוב ייושמו ב- Python בהשוואה לשפות נוקשה יותר, סטטיות יותר, אופי דינמי של Python מאפשר יישום יותר של דפוסים רבים, תוך הצגת הצורך בתרגול קידוד ממושמע.

מכיוון שפייתון היא כה חזקה וגמישה, אנו זקוקים לכמה כללים או דפוסים כאשר אנו מתכנתים בה.ללא עקרונות מנחים אלה, בסיסי קוד יכולים להפוך במהירות ללא מחוסנים וקשה לשמור על תבניות עיצוב.

שלושת קטגוריות של תבניות עיצוב

דפוסי עיצוב מאורגנים באופן מסורתי לשלוש קטגוריות עיקריות, כל אחת מהן מתייחסת להיבטים שונים של עיצוב תוכנה.הבנת קטגוריות אלה מסייעת למפתחים לבחור את התבנית המתאימה לאתגרים הספציפיים שלהם.

תבניות יצירה

תבניות הבריאה מתמקדות בתהליך יצירת האובייקט, מתן מנגנונים המגדילים גמישות ושימוש חוזר של הקוד הקיים.תבניות אלה מופשטות את תהליך ההרגעה, מה שהופך מערכות עצמאיות מאיך נוצרות, מורכבות ומייצגות.

במקום ליצור אובייקטים ישירות באמצעות בונה, תבניות יצירה מספקות יותר שליטה וגמישות על תהליך הבריאה. גישה זו היא בעלת ערך במיוחד כאשר לוגיקה הבריאה מורכבת, כאשר אתה צריך לשלוט באיזה שיעור מקבל מיידיות, או כאשר אתה רוצה לנהל הקצאת משאבים ביעילות רבה יותר.

דפוסים יצירה משותפים ב- Python כוללים Singleton, Factory Method, Factory, Builder, Prototype. כל אחד מהם משרת מטרה ייחודית בניהול מורכבות יצירת אובייקטים.

תבניות מבניות

תבניות עיצוב סטרקטידור להתמקד בהרכב של כיתות או אובייקטים כדי ליצור מבנים גדולים יותר, מורכבים יותר, עוזר לארגן ולנהל מערכות יחסים בין אובייקטים להשגת גמישות רבה יותר, התחדשות, ותחזוקתיות.דפוסים אלה מודאגים כיצד שיעורים וחפצים מורכבים כדי ליצור מבנים גדולים יותר תוך שמירה על מבנים גמישים ויעילים אלה.

תבניות סטרטואלריות עוזרות להבטיח כי כאשר חלק אחד של מערכת משתנה, המבנה כולו אינו צריך להיות שונה.הם להקל על עיצוב מערכות שבו רכיבים ניתן להחליף בקלות או להרחיב מבלי להשפיע על חלקים אחרים של היישום.תבניות מבניות נפוצות כוללות הסתגלות, גשר, Composite, דקורטיביor, Facade, משקל זבוב, ו Proxy.

דפוס התנהגות

דפוסים התנהגותיים מודאגים מאלגוריתמים ומשימה של אחריות בין אובייקטים.הם מתארים לא רק דפוסים של אובייקטים או שיעורים אלא גם את דפוסי התקשורת ביניהם.תבניות אלה מאפייןות זרימה של שליטה מורכבת שקשה לעקוב אחריהם בזמן ריצה.

Observer מתייחס לתקשורת, המאפשר חלקים מרובים של מערכת להגיב באופן אוטומטי לאירועים או שינויים ממשלתיים.תבניות התנהגות אחרות כוללות אסטרטגיה, פיקוד, מאיץ, Mediator, Memento, מדינה, תבנית, מבקרים ושרשרת של אחריות.

ה- Singleton Pattern: One Instance to Rule Them All

תבנית ה- Singleton מבטיחה לכיתה יש רק מקרה אחד בכל תוכנית ומספקת נקודת גישה גלובלית, המשמש בדרך כלל לניהול משאבים משותפים כמו מסדי נתונים, מערכות אחסון או מנהלי קבצים.תבנית זו היא אחד הדפוסים המדוברים ביותר ולעתים שנויים במחלוקת בפיתוח תוכנה.

מתי להשתמש Singleton

דפוס Singleton שימושי במיוחד כאשר אתה צריך בדיוק מקרה אחד של מחלקה לשלוט משאבים כגון חיבורי מסד נתונים, מנהלי תצורה או מערכות logging.התבנית מבטיחה כי כל הקוד באמצעות המקרה בכיתה עובד עם אותו אובייקט, הבטחת עקביות על פני היישום.

מקרים לשימוש לגיטימי עבור סינגלים ב- Python כוללים:

  • ממשקים קשיחים המייצגים משאבים פיזיים ייחודיים, כגון מצלמה אחת, מדפסת אחת או ממשק GPIO אחד, שבו אחד מהם מדגמי את זה במדויק
  • שכבות Caching שבו אתה רוצה שכאב משותף אחד משותף על פני היישום שלך
  • בריכות קישור שבו אתה רוצה להגביל ולשתף משאבים יקרים, עם הבריכה עצמה להיות רווקה, אם כי המשאבים שהיא מנהלת הם לא
  • מערכות ניהול קונפדרציה צריכות לשמור על הגדרות עקביות לאורך היישום
  • מערכות אינטגרציה שבהן ניהול התגים מרכזי הוא חיוני

תצלום: Singleton in Python

ניתן ליישם את הכיתה הבודדטון בדרכים שונות ב- Python, כולל מעמד בסיס, עיצוב וגישות מטאפור, עם מטאפורה להיות מתאים ביותר למטרה זו.כל שיטת יישום יש יתרונות משלה ומסחר.

יישום קלאסי באמצעות ההרחבה (FLT:0) new nova 03.

class DatabaseConnection:
 _instance = None

 def __new__(cls):
 if cls._instance is None:
 cls._instance = super(DatabaseConnection, cls).__new__(cls)
 cls._instance._initialize()
 return cls._instance

 def _initialize(self):
 self.connection = None
 self.host = "localhost"
 self.port = 5432

 def connect(self):
 if not self.connection:
 self.connection = f"Connected to {self.host}:{self.port}"
 return self.connection

יישום בטוח חוט משתמש אובייקט מנעול המסנכרן חוטים במהלך הגישה הראשונה ל Singleton.זה חיוני ביישומים רב-תקרא שבו תנאי גזע יכולים להוביל למקרים רבים להיווצר:

from threading import Lock

class ThreadSafeSingleton:
 _instance = None
 _lock = Lock()

 def __new__(cls):
 if cls._instance is None:
 with cls._lock:
 if cls._instance is None:
 cls._instance = super().__new__(cls)
 return cls._instance

אפשרויות ל- Pythonic Alternative:מודול-Level Singletons

מערכת המודול של Python היא עצמה מנגנון יחיד: כאשר אתה לייבא מודול, Python מבצעת אותו פעם אחת ומקפיד את התוצאה בסימס.מודולים, עם כל יבוא אחר-כך חוזר אובייקט המודול המצופה, לא חדש.זה הופך את המודולים דרך טבעית ופייתיתית ליישם התנהגות בודדת.

Python מייבאת מודול רק פעם אחת, מה שהופך כל דבר מוגדר בתוך מודול ביעילות סינגלטון.גישה זו היא לעתים קרובות פשוטה יותר והחזקה יותר מאשר יישום מחלקה רשמית של Singleton:

# config.py
class _Config:
 def __init__(self):
 self.database_url = "postgresql://localhost/mydb"
 self.api_key = "secret_key"
 self.debug_mode = True

 def update_setting(self, key, value):
 setattr(self, key, value)

# Create the singleton instance
config = _Config()

# In other files, simply import:
# from config import config

דפוס הטון בדרך כלל אינו הגיוני בפייתון בצורתו הטהורה ביותר; במקום זאת, הוא בדרך כלל הגיוני יותר להפוך מקרה אחד של כיתה ולהקצות את אותו מקרה למשתנה גלובלי במודול.גישה זו היא יותר שקופה, קלה יותר לבדיקה, ויישר טוב יותר עם הפילוסופיה של פייתון.

Singleton Drawbacks and Considerations

מפתחים רבים רואים את תבנית ה- Singleton נוגדת-מאטר, ולכן השימוש בו הוא על הירידה בקוד פייתון.יש כמה חששות לגיטימיים:

  • בשל המצב הגלובלי שלה והפיכה הדוקה עם חלקים אחרים של בסיס הקוד, דפוס ה- Singleton יכול להפוך את יחידת הבדיקה מאתגרת, עם לעג או החלפת המקרה של ה-oneton להיות מגושם.
  • בסביבות מרובות-הנקראות, דפוס ה- Singleton יכול להציג בעיות מטבעות אם לא ייושמו בזהירות, עם חוטים מרובים עשויים ליצור מקרים מרובים ללא סינכרוניזציה נאותה.
  • סינגלים יוצרים תלות נסתרת שהופכת את הקוד קשה יותר להבנה ולתחזק
  • הם מפרים את עקרון האחריות הבודד על ידי ניהול ההיגיון העסקי שלהם ואת המימוש שלהם
  • סינגלים מקשים להרחיב או לשנות התנהגות באמצעות ירושה

שקול באמצעות הזרקת תלות במקום, כפי שהוא יכול להיות נקי יותר, או להשתמש במקרה ברמת מודול. חלופות אלה לעתים קרובות לספק את אותם היתרונות ללא החסרונות.

עיצוב אובייקטים גמישים: Creative Object Creation

תבנית המפעל מספקת ממשק ליצירת אובייקטים מבלי לציין את השיעורים המדויקים שלהם, קידום הפיכה חופשית וביצוע הקוד גמיש יותר לשינויים.תבנית זו אינה ניתנת לערעור כאשר אתה צריך ליצור אובייקטים, אבל רוצה למחוק את ההיגיון הבריאה מהקוד המשתמש באובייקטים אלה.

המפעל מתרכז כיצד חפצים נוצרים, מסתירים לוגיקה מיידית וצמצום ההפיכה הדוקה בין רכיבים.על ידי ריכוז יצירת אובייקטים, דפוס המפעל מקל על להציג סוגים חדשים מבלי לשנות קוד קיים.

מפעל פשוט

תבנית המפעל הפשוטה מדגימה יצירת אובייקטים בתפקוד ייעודי או בכיתה.כאן דוגמה מעשית ליצירת סוגים שונים של חיבורי מסד נתונים:

from abc import ABC, abstractmethod

class Database(ABC):
 @abstractmethod
 def connect(self):
 pass

 @abstractmethod
 def query(self, sql):
 pass

class PostgreSQLDatabase(Database):
 def connect(self):
 return "Connected to PostgreSQL"

 def query(self, sql):
 return f"PostgreSQL executing: {sql}"

class MySQLDatabase(Database):
 def connect(self):
 return "Connected to MySQL"

 def query(self, sql):
 return f"MySQL executing: {sql}"

class MongoDBDatabase(Database):
 def connect(self):
 return "Connected to MongoDB"

 def query(self, sql):
 return f"MongoDB executing: {sql}"

class DatabaseFactory:
 @staticmethod
 def create_database(db_type):
 databases = {
 'postgresql': PostgreSQLDatabase,
 'mysql': MySQLDatabase,
 'mongodb': MongoDBDatabase
 }

 db_class = databases.get(db_type.lower())
 if not db_class:
 raise ValueError(f"Unknown database type: {db_type}")

 return db_class()

# Usage
db = DatabaseFactory.create_database('postgresql')
print(db.connect())
print(db.query("SELECT * FROM users"))

שיטת המפעל

שיטת המפעל מספקת ממשק ליצירת אובייקטים בסופר-מעמד, אך מאפשרת ל- subclasses לשנות את סוג האובייקטים שייצרו.וריאציות אלה מספקות אפילו גמישות רבה יותר על ידי ניתוק הרגעה ל- subclasses:

from abc import ABC, abstractmethod

class DocumentProcessor(ABC):
 @abstractmethod
 def create_parser(self):
 """Factory method to be implemented by subclasses"""
 pass

 def process_document(self, content):
 parser = self.create_parser()
 return parser.parse(content)

class Parser(ABC):
 @abstractmethod
 def parse(self, content):
 pass

class JSONParser(Parser):
 def parse(self, content):
 return f"Parsing JSON: {content}"

class XMLParser(Parser):
 def parse(self, content):
 return f"Parsing XML: {content}"

class CSVParser(Parser):
 def parse(self, content):
 return f"Parsing CSV: {content}"

class JSONDocumentProcessor(DocumentProcessor):
 def create_parser(self):
 return JSONParser()

class XMLDocumentProcessor(DocumentProcessor):
 def create_parser(self):
 return XMLParser()

# Usage
json_processor = JSONDocumentProcessor()
result = json_processor.process_document('{"name": "John"}')
print(result)

היתרונות של Factory Pattern

תבנית המפעל מציעה מספר יתרונות משכנעים:

  • (ה) ,0) ל"לווז קופלינג'ר 1: קוד הלקוח אינו צריך לדעת את המעמדות הבטניים שהפכו מידיים
  • (ב) לוגיקה של יצירת אובייקטים היא מרכזית במקום אחד
  • (ב) ניתן להוסיף (ב) ל-[[1924]]: "[[1924]]]]" ([[1924]]]]]]
  • (ב) ⁇ :0) ⁇ (ב) , קל לעבור בין יישום אחר במשרה מלאה
  • (ב) ⁇ :0) ניתן להחדיר בקלות חפצים מנוק למטרות בדיקה

ארכיון תגיות: Event-Driven Architecture

תבנית Observer מגדירה מנגנון מנויים להודיע פריטים מרובים על כל אירוע שקורה לאובייקט שהם רואים.תבנית זו היא יסוד לתכנות המונעות על ידי אירועים ומשמשת באופן נרחב במסגרות GUI, מערכות בזמן אמת ויישומים תגובתיים.

הבנת תבנית ה- Observer

דפוס המשקיף קובע תלות חד-אנושית בין אובייקטים.כאשר הנושא (הבלתי-אפשרי) משנה את המדינה, כל התלויים שלו (הנצנצפים) מאומתים ומעודכן באופן אוטומטי.זה מקטין את הנושא מהמשקיפים שלו, ומאפשר להם להשתנות באופן עצמאי.

הנה יישום מקיף של תבנית ה- Observer:

from abc import ABC, abstractmethod
from typing import List

class Observer(ABC):
 @abstractmethod
 def update(self, subject):
 """Receive update from subject"""
 pass

class Subject(ABC):
 def __init__(self):
 self._observers: List[Observer] = []

 def attach(self, observer: Observer):
 """Attach an observer to the subject"""
 if observer not in self._observers:
 self._observers.append(observer)

 def detach(self, observer: Observer):
 """Detach an observer from the subject"""
 try:
 self._observers.remove(observer)
 except ValueError:
 pass

 def notify(self):
 """Notify all observers about an event"""
 for observer in self._observers:
 observer.update(self)

class StockPrice(Subject):
 def __init__(self, symbol: str, price: float):
 super().__init__()
 self._symbol = symbol
 self._price = price

 @property
 def symbol(self):
 return self._symbol

 @property
 def price(self):
 return self._price

 @price.setter
 def price(self, new_price: float):
 if new_price != self._price:
 self._price = new_price
 self.notify()

class StockDisplay(Observer):
 def __init__(self, name: str):
 self._name = name

 def update(self, subject: StockPrice):
 print(f"{self._name}: {subject.symbol} is now ${subject.price:.2f}")

class StockAlert(Observer):
 def __init__(self, threshold: float):
 self._threshold = threshold

 def update(self, subject: StockPrice):
 if subject.price > self._threshold:
 print(f"ALERT: {subject.symbol} exceeded ${self._threshold}! Current: ${subject.price:.2f}")

# Usage
apple_stock = StockPrice("AAPL", 150.00)

display1 = StockDisplay("Display 1")
display2 = StockDisplay("Display 2")
alert = StockAlert(160.00)

apple_stock.attach(display1)
apple_stock.attach(display2)
apple_stock.attach(alert)

apple_stock.price = 155.50
apple_stock.price = 162.00

יישומים אמיתיים

תבנית ה- Observer משמשת רבות בפיתוח תוכנה מודרני:

  • (ב) [15] ,9) , ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇
  • (ב) ⁇ :0)Model-View-Controller (MVCigtureFLT) 1:1: תצוגות רואות מודלים לשינויים בנתונים
  • (FLT:0) הזנת נתונים בזמן אמת 1: מחירי מניות, עדכוני מזג אוויר, הודעות מדיה חברתית
  • (ב) ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇
  • (ב) ,0) ,Publish-Subscribe SystemsFreaLT:1: תורי הודעות ואוטובוסים אירועים

תבנית דקורטיבית: הפעלת פונקציונליות דינמית

תבנית דקורטיבית מאפשרת הוספת התנהגות לאובייקט – כניסה, צ'ינג, אימות, דחייה – מבלי לשנות את המעמד של האובייקט וללא תת-מחלקה.תבנית זו מספקת אלטרנטיבה גמישה להשתתפות בפונקציונליות הרחבה.

פייתונים לעומת תבנית דקורטיבית

פייתון 'המס @decorator ותבנית העיצוב דקורטיבי הם באופן קונספטואלי זהה - הן התנהגות עוטפת סביב שיחה קיימת ללא שינוי זה, עם הסינמס של Python עושה את התבנית תכונה שפה-native.זה הופך את Python מתאים במיוחד עבור יישום פונקציונליות עיצוב.

הנה דוגמה לשימוש ב-Syntax של Python:

import time
import functools

def timing_decorator(func):
 @functools.wraps(func)
 def wrapper(*args, **kwargs):
 start_time = time.time()
 result = func(*args, **kwargs)
 end_time = time.time()
 print(f"{func.__name__} took {end_time - start_time:.4f} seconds")
 return result
 return wrapper

def cache_decorator(func):
 cache = {}

 @functools.wraps(func)
 def wrapper(*args):
 if args in cache:
 print(f"Returning cached result for {args}")
 return cache[args]
 result = func(*args)
 cache[args] = result
 return result
 return wrapper

def retry_decorator(max_attempts=3):
 def decorator(func):
 @functools.wraps(func)
 def wrapper(*args, **kwargs):
 for attempt in range(max_attempts):
 try:
 return func(*args, **kwargs)
 except Exception as e:
 if attempt == max_attempts - 1:
 raise
 print(f"Attempt {attempt + 1} failed: {e}. Retrying...")
 return None
 return wrapper
 return decorator

@timing_decorator
@cache_decorator
def fibonacci(n):
 if n < 2:
 return n
 return fibonacci(n-1) + fibonacci(n-2)

@retry_decorator(max_attempts=3)
def unreliable_api_call():
 import random
 if random.random() < 0.7:
 raise ConnectionError("API temporarily unavailable")
 return "Success!"

# Usage
print(fibonacci(10))
print(fibonacci(10)) # This will use cached result

תבנית מבוססת Class-based Decorator

ניתן ליישם את תבנית ה-Tor המסורתית באמצעות כיתות, אשר שימושיות עבור תרחישים מורכבים יותר:

from abc import ABC, abstractmethod

class Coffee(ABC):
 @abstractmethod
 def cost(self):
 pass

 @abstractmethod
 def description(self):
 pass

class SimpleCoffee(Coffee):
 def cost(self):
 return 2.00

 def description(self):
 return "Simple coffee"

class CoffeeDecorator(Coffee):
 def __init__(self, coffee: Coffee):
 self._coffee = coffee

 def cost(self):
 return self._coffee.cost()

 def description(self):
 return self._coffee.description()

class MilkDecorator(CoffeeDecorator):
 def cost(self):
 return self._coffee.cost() + 0.50

 def description(self):
 return self._coffee.description() + ", milk"

class SugarDecorator(CoffeeDecorator):
 def cost(self):
 return self._coffee.cost() + 0.25

 def description(self):
 return self._coffee.description() + ", sugar"

class WhippedCreamDecorator(CoffeeDecorator):
 def cost(self):
 return self._coffee.cost() + 0.75

 def description(self):
 return self._coffee.description() + ", whipped cream"

# Usage
coffee = SimpleCoffee()
print(f"{coffee.description()}: ${coffee.cost():.2f}")

coffee_with_milk = MilkDecorator(coffee)
print(f"{coffee_with_milk.description()}: ${coffee_with_milk.cost():.2f}")

fancy_coffee = WhippedCreamDecorator(SugarDecorator(MilkDecorator(SimpleCoffee())))
print(f"{fancy_coffee.description()}: ${fancy_coffee.cost():.2f}")

אסטרטגיה: Algorithms

דפוס האסטרטגיה מאפשר לך להגדיר משפחה של אלגוריתמים, לבודד כל אחד, ולהפוך אותם להחלפה בזמני ריצה, להניע התנהגות לשיעורי אסטרטגיה נפרדים או לפונקציות במקום לכתוב בלוקים מצביים גדולים.תבנית זו חיונית לכתיבה קוד גמיש, אמין שיכול להתאים לדרישות שונות.

דפוס האסטרטגיה מגדיר משפחה של אלגוריתמים, מעמיד כל אחד מהם לכיתה נפרדת, והופך את האובייקטים שלהם להחלפה.זה מאפשר לבחור אלגוריתמים בזמן ריצה בהתבסס על ההקשר או התצורה.

יישום אסטרטגיה דפוס

הנה דוגמה מקיפה המדגימה את תבנית האסטרטגיה לעיבוד תשלומים:

from abc import ABC, abstractmethod
from typing import Protocol

class PaymentStrategy(Protocol):
 def pay(self, amount: float) -> str:
 """Process payment and return confirmation"""
 ...

class CreditCardPayment:
 def __init__(self, card_number: str, cvv: str, expiry: str):
 self.card_number = card_number
 self.cvv = cvv
 self.expiry = expiry

 def pay(self, amount: float) -> str:
 # Simulate payment processing
 masked_card = f"****-****-****-{self.card_number[-4:]}"
 return f"Paid ${amount:.2f} using Credit Card {masked_card}"

class PayPalPayment:
 def __init__(self, email: str):
 self.email = email

 def pay(self, amount: float) -> str:
 return f"Paid ${amount:.2f} using PayPal account {self.email}"

class CryptocurrencyPayment:
 def __init__(self, wallet_address: str, currency: str = "BTC"):
 self.wallet_address = wallet_address
 self.currency = currency

 def pay(self, amount: float) -> str:
 return f"Paid ${amount:.2f} using {self.currency} to wallet {self.wallet_address[:10]}..."

class BankTransferPayment:
 def __init__(self, account_number: str, routing_number: str):
 self.account_number = account_number
 self.routing_number = routing_number

 def pay(self, amount: float) -> str:
 masked_account = f"****{self.account_number[-4:]}"
 return f"Paid ${amount:.2f} via bank transfer from account {masked_account}"

class ShoppingCart:
 def __init__(self):
 self.items = []
 self.payment_strategy = None

 def add_item(self, item: str, price: float):
 self.items.append({'item': item, 'price': price})

 def set_payment_strategy(self, strategy: PaymentStrategy):
 self.payment_strategy = strategy

 def calculate_total(self) -> float:
 return sum(item['price'] for item in self.items)

 def checkout(self) -> str:
 if not self.payment_strategy:
 raise ValueError("Payment strategy not set")

 total = self.calculate_total()
 return self.payment_strategy.pay(total)

# Usage
cart = ShoppingCart()
cart.add_item("Laptop", 999.99)
cart.add_item("Mouse", 29.99)
cart.add_item("Keyboard", 79.99)

# Pay with credit card
cart.set_payment_strategy(CreditCardPayment("1234567890123456", "123", "12/25"))
print(cart.checkout())

# Change strategy to PayPal
cart.set_payment_strategy(PayPalPayment("[email protected]"))
print(cart.checkout())

# Change strategy to Cryptocurrency
cart.set_payment_strategy(CryptocurrencyPayment("1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa"))
print(cart.checkout())

אסטרטגיה פייתיתית עם פונקציות

פונקציות המדרגה הראשונה של Python מאפשרות יישום יותר של דפוס האסטרטגיה:

def discount_none(total):
 return total

def discount_percentage(percentage):
 def apply_discount(total):
 return total * (1 - percentage / 100)
 return apply_discount

def discount_fixed_amount(amount):
 def apply_discount(total):
 return max(0, total - amount)
 return apply_discount

def discount_bulk(threshold, discount_percentage):
 def apply_discount(total):
 if total >= threshold:
 return total * (1 - discount_percentage / 100)
 return total
 return apply_discount

class Order:
 def __init__(self, total: float):
 self.total = total
 self.discount_strategy = discount_none

 def set_discount_strategy(self, strategy):
 self.discount_strategy = strategy

 def calculate_final_price(self):
 return self.discount_strategy(self.total)

# Usage
order = Order(100.00)
print(f"No discount: ${order.calculate_final_price():.2f}")

order.set_discount_strategy(discount_percentage(10))
print(f"10% discount: ${order.calculate_final_price():.2f}")

order.set_discount_strategy(discount_fixed_amount(15))
print(f"$15 off: ${order.calculate_final_price():.2f}")

order.set_discount_strategy(discount_bulk(50, 20))
print(f"Bulk discount: ${order.calculate_final_price():.2f}")

עיצוב: בניית אובייקטים מורכבים

דפוס ה-Build מאפשר לך לבנות אובייקטים מורכבים צעד אחר צעד, המאפשר לך לייצר סוגים שונים וייצוגים של אובייקט באמצעות אותו קוד בנייה.תבנית זו היא שימושית במיוחד כאשר אובייקט דורש אפשרויות תצורה רבות או כאשר תהליך הבנייה כרוך במספר שלבים.

מתי להשתמש בתבנית Builder

דפוס ה-Mader מאיר בתרחישים שבהם:

  • בניית אובייקטים דורשת פרמטרים אופציונליים רבים
  • תהליך הבנייה חייב לעקוב אחר רצף ספציפי
  • אתה צריך ליצור ייצוגים שונים של אותו אובייקט
  • פרמטרים של בניית יוצרים "מבנים מאומנים" נגד-פטרטר
  • יצירת אובייקטים כוללת היגיון מורכב שיש להפריד מהאובייקט עצמו

יישום תבנית בונה ב Python

הנה יישום מעשי לבניית אובייקטים של חיפוש מסד נתונים:

from typing import List, Optional
from dataclasses import dataclass, field

@dataclass
class Query:
 table: str
 columns: List[str]
 where_clauses: List[str]
 joins: List[str]
 order_by: Optional[str]
 limit: Optional[int]
 offset: Optional[int]

 def to_sql(self) -> str:
 # Select clause
 cols = ", ".join(self.columns) if self.columns else "*"
 sql = f"SELECT {cols} FROM {self.table}"

 # Joins
 if self.joins:
 sql += " " + " ".join(self.joins)

 # Where clause
 if self.where_clauses:
 sql += " WHERE " + " AND ".join(self.where_clauses)

 # Order by
 if self.order_by:
 sql += f" ORDER BY {self.order_by}"

 # Limit and offset
 if self.limit:
 sql += f" LIMIT {self.limit}"
 if self.offset:
 sql += f" OFFSET {self.offset}"

 return sql

class QueryBuilder:
 def __init__(self):
 self._table: Optional[str] = None
 self._columns: List[str] = []
 self._where_clauses: List[str] = []
 self._joins: List[str] = []
 self._order_by: Optional[str] = None
 self._limit: Optional[int] = None
 self._offset: Optional[int] = None

 def table(self, table_name: str) -> 'QueryBuilder':
 self._table = table_name
 return self

 def select(self, *columns: str) -> 'QueryBuilder':
 self._columns.extend(columns)
 return self

 def where(self, condition: str) -> 'QueryBuilder':
 self._where_clauses.append(condition)
 return self

 def join(self, join_clause: str) -> 'QueryBuilder':
 self._joins.append(join_clause)
 return self

 def order_by(self, column: str, direction: str = "ASC") -> 'QueryBuilder':
 self._order_by = f"{column} {direction}"
 return self

 def limit(self, limit: int) -> 'QueryBuilder':
 self._limit = limit
 return self

 def offset(self, offset: int) -> 'QueryBuilder':
 self._offset = offset
 return self

 def build(self) -> Query:
 if not self._table:
 raise ValueError("Table name is required")

 return Query(
 table=self._table,
 columns=self._columns,
 where_clauses=self._where_clauses,
 joins=self._joins,
 order_by=self._order_by,
 limit=self._limit,
 offset=self._offset
 )

 def reset(self) -> 'QueryBuilder':
 self.__init__()
 return self

# Usage
builder = QueryBuilder()

# Build a simple query
query1 = (builder
 .table("users")
 .select("id", "name", "email")
 .where("age > 18")
 .where("status = 'active'")
 .order_by("name", "ASC")
 .limit(10)
 .build())

print(query1.to_sql())

# Build a complex query with joins
builder.reset()
query2 = (builder
 .table("orders")
 .select("orders.id", "users.name", "products.title", "orders.total")
 .join("INNER JOIN users ON orders.user_id = users.id")
 .join("INNER JOIN products ON orders.product_id = products.id")
 .where("orders.status = 'completed'")
 .where("orders.total > 100")
 .order_by("orders.created_at", "DESC")
 .limit(20)
 .offset(10)
 .build())

print(query2.to_sql())

יתרונות ממשק Fluent

דפוס היצרנים לעתים קרובות ליישם ממשק שוטה (שרשרת מקול), המספק מספר יתרונות:

  • (ב) ,0) קראיות (קרא: 1): קוד קורא כמו שפה טבעית
  • (ב) ⁇ (ב) ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇
  • (ב) ,0) ,(ה) , ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇
  • (ב) לוגיקה מבנית יכולה לאמת את האובייקט לפני הבריאה
  • (ב) ניתן להשתמש ב-[[1924]] ב[[1924]], [[1924]], [[1924]], [[1924]]

תבנית: להפוך את הממשקים הלא תואמים לעבוד יחד

דפוס הסתגלות מאפשר לאובייקטים עם ממשקים לא עולים בקנה אחד עם שיתוף פעולה.תבנית מבנית זו פועלת כגשר בין שני ממשקים לא עולים בקנה אחד, המאפשרים לשיעורים לעבוד יחד שלא יכלו אחרת בשל ממשקים לא תואמים.

שיטת הסתגלות היא דפוס עיצוב מבני המאפשר לך ליצור שני ממשקים לא עולים בקנה אחד עם יצירת גשר ביניהם.זה חשוב במיוחד בעת שילוב ספריות של צד שלישי, קוד מורשת או API חיצוני לתוך היישום שלך.

יישום אמיתי-עולם

שקול תרחיש שבו אתה צריך לשלב שער תשלום מרובים עם ממשקים שונים:

from abc import ABC, abstractmethod

# Target interface that our application expects
class PaymentProcessor(ABC):
 @abstractmethod
 def process_payment(self, amount: float, currency: str) -> dict:
 pass

# Adaptee 1: Stripe API (incompatible interface)
class StripeAPI:
 def create_charge(self, amount_cents: int, currency_code: str, source: str):
 return {
 'id': 'ch_stripe_123',
 'amount': amount_cents,
 'currency': currency_code,
 'status': 'succeeded'
 }

# Adaptee 2: PayPal API (different incompatible interface)
class PayPalAPI:
 def make_payment(self, total: float, currency_type: str, account: str):
 return {
 'transaction_id': 'pp_456',
 'total_amount': total,
 'currency': currency_type,
 'state': 'approved'
 }

# Adaptee 3: Square API (yet another incompatible interface)
class SquareAPI:
 def charge_card(self, money_amount: dict, card_token: str):
 return {
 'payment_id': 'sq_789',
 'amount_money': money_amount,
 'status': 'COMPLETED'
 }

# Adapter for Stripe
class StripeAdapter(PaymentProcessor):
 def __init__(self, stripe_api: StripeAPI):
 self.stripe = stripe_api

 def process_payment(self, amount: float, currency: str) -> dict:
 # Convert dollars to cents for Stripe
 amount_cents = int(amount * 100)

 # Call Stripe API with adapted parameters
 result = self.stripe.create_charge(
 amount_cents=amount_cents,
 currency_code=currency.upper(),
 source='tok_visa'
 )

 # Adapt the response to our standard format
 return {
 'success': result['status'] == 'succeeded',
 'transaction_id': result['id'],
 'amount': amount,
 'currency': currency,
 'provider': 'Stripe'
 }

# Adapter for PayPal
class PayPalAdapter(PaymentProcessor):
 def __init__(self, paypal_api: PayPalAPI):
 self.paypal = paypal_api

 def process_payment(self, amount: float, currency: str) -> dict:
 result = self.paypal.make_payment(
 total=amount,
 currency_type=currency.upper(),
 account='[email protected]'
 )

 return {
 'success': result['state'] == 'approved',
 'transaction_id': result['transaction_id'],
 'amount': amount,
 'currency': currency,
 'provider': 'PayPal'
 }

# Adapter for Square
class SquareAdapter(PaymentProcessor):
 def __init__(self, square_api: SquareAPI):
 self.square = square_api

 def process_payment(self, amount: float, currency: str) -> dict:
 money_amount = {
 'amount': int(amount * 100),
 'currency': currency.upper()
 }

 result = self.square.charge_card(
 money_amount=money_amount,
 card_token='cnon:card-token'
 )

 return {
 'success': result['status'] == 'COMPLETED',
 'transaction_id': result['payment_id'],
 'amount': amount,
 'currency': currency,
 'provider': 'Square'
 }

# Client code that works with the unified interface
class PaymentService:
 def __init__(self, processor: PaymentProcessor):
 self.processor = processor

 def charge_customer(self, amount: float, currency: str = 'USD'):
 result = self.processor.process_payment(amount, currency)

 if result['success']:
 print(f"✓ Payment successful via {result['provider']}")
 print(f" Transaction ID: {result['transaction_id']}")
 print(f" Amount: {result['amount']} {result['currency']}")
 else:
 print(f"✗ Payment failed")

 return result

# Usage - all payment gateways work through the same interface
stripe_processor = StripeAdapter(StripeAPI())
paypal_processor = PayPalAdapter(PayPalAPI())
square_processor = SquareAdapter(SquareAPI())

# Process payments using different providers
service = PaymentService(stripe_processor)
service.charge_customer(99.99)

service = PaymentService(paypal_processor)
service.charge_customer(149.99)

service = PaymentService(square_processor)
service.charge_customer(199.99)

תבנית דפוס: Defining Algorithm Skeletons

שיטת התבנית מגדירה את השלד של אלגוריתם בסופר-class אבל מאפשר תת-classes לעקוף שלבים ספציפיים של האלגוריתם מבלי לשנות את המבנה שלו.תבנית התנהגות זו מצוינת לאכיפת תהליך עקבי תוך מתן התאמה אישית בנקודות ספציפיות.

שיטת ההתקנה

from abc import ABC, abstractmethod
import time

class DataProcessor(ABC):
 """Template class defining the data processing algorithm"""

 def process(self, data):
 """Template method defining the algorithm structure"""
 print("Starting data processing pipeline...")

 # Step 1: Validate
 if not self.validate(data):
 raise ValueError("Data validation failed")

 # Step 2: Extract
 extracted = self.extract(data)

 # Step 3: Transform
 transformed = self.transform(extracted)

 # Step 4: Load
 result = self.load(transformed)

 # Step 5: Cleanup (optional hook)
 self.cleanup()

 print("Data processing completed successfully")
 return result

 @abstractmethod
 def validate(self, data) -> bool:
 """Validate input data - must be implemented by subclasses"""
 pass

 @abstractmethod
 def extract(self, data):
 """Extract relevant data - must be implemented by subclasses"""
 pass

 @abstractmethod
 def transform(self, data):
 """Transform data - must be implemented by subclasses"""
 pass

 @abstractmethod
 def load(self, data):
 """Load processed data - must be implemented by subclasses"""
 pass

 def cleanup(self):
 """Optional hook method - can be overridden by subclasses"""
 print("Performing default cleanup...")

class CSVDataProcessor(DataProcessor):
 def validate(self, data) -> bool:
 print("Validating CSV data...")
 return isinstance(data, str) and len(data) > 0

 def extract(self, data):
 print("Extracting CSV data...")
 lines = data.strip().split('n')
 headers = lines[0].split(',')
 rows = [line.split(',') for line in lines[1:]]
 return {'headers': headers, 'rows': rows}

 def transform(self, data):
 print("Transforming CSV data...")
 headers = data['headers']
 rows = data['rows']
 return [dict(zip(headers, row)) for row in rows]

 def load(self, data):
 print(f"Loading {len(data)} CSV records...")
 return data

 def cleanup(self):
 print("Cleaning up CSV processing resources...")

class JSONDataProcessor(DataProcessor):
 def validate(self, data) -> bool:
 print("Validating JSON data...")
 import json
 try:
 json.loads(data)
 return True
 except:
 return False

 def extract(self, data):
 print("Extracting JSON data...")
 import json
 return json.loads(data)

 def transform(self, data):
 print("Transforming JSON data...")
 if isinstance(data, list):
 return [self._flatten_dict(item) for item in data]
 return [self._flatten_dict(data)]

 def _flatten_dict(self, d, parent_key='', sep='_'):
 items = []
 for k, v in d.items():
 new_key = f"{parent_key}{sep}{k}" if parent_key else k
 if isinstance(v, dict):
 items.extend(self._flatten_dict(v, new_key, sep=sep).items())
 else:
 items.append((new_key, v))
 return dict(items)

 def load(self, data):
 print(f"Loading {len(data)} JSON records...")
 return data

class XMLDataProcessor(DataProcessor):
 def validate(self, data) -> bool:
 print("Validating XML data...")
 return data.strip().startswith('')

 def extract(self, data):
 print("Extracting XML data...")
 # Simplified XML parsing
 return {'xml_content': data}

 def transform(self, data):
 print("Transforming XML data...")
 return {'processed_xml': data['xml_content']}

 def load(self, data):
 print("Loading XML data...")
 return data

 def cleanup(self):
 print("Cleaning up XML processing resources...")
 print("Closing XML parser...")

# Usage
csv_data = """name,age,city
John,30,New York
Jane,25,Los Angeles
Bob,35,Chicago"""

json_data = '''[
 {"name": "John", "age": 30, "address": {"city": "New York"}},
 {"name": "Jane", "age": 25, "address": {"city": "Los Angeles"}}
]'''

xml_data = "John30"

# Process CSV
csv_processor = CSVDataProcessor()
csv_result = csv_processor.process(csv_data)
print(f"CSV Result: {csv_result}n")

# Process JSON
json_processor = JSONDataProcessor()
json_result = json_processor.process(json_data)
print(f"JSON Result: {json_result}n")

# Process XML
xml_processor = XMLDataProcessor()
xml_result = xml_processor.process(xml_data)
print(f"XML Result: {xml_result}")

מתי להשתמש בתבניות עיצוב

לדעת מתי להשתמש בדפוסי עיצוב הוא חיוני לתכנון תוכנה יעיל, במיוחד כאשר אתה נתקל בבעיות עיצוב חוזרות שיש להם פתרונות מבוססים היטב, כמו תבניות עיצוב לספק גישות נבדקות ומוכחות לאתגרים עיצוב תוכנה משותף.

שימוש במקרים

השתמש בדפוסי עיצוב כדי לקדם את יכולת ה-קוד, גמישות ותחזוקתיות, כפי שהם מסייעים ב-structing Code באופן שהופך אותו קל יותר לשנות ולהרחיב את דרישות להתפתח.

  • אתה נתקל בבעיה שמתאימה לכוונת דפוס ידוע
  • התבנית מספקת יתרונות ברורים על פני פתרון פשוט יותר
  • הצוות שלך מבין את התבנית ויכול לשמור עליה
  • המורכבות הנוספת מוצדקת על ידי גמישות משופרת או שמירה על
  • אתה רוצה לשפר את התקשורת בין חברי הצוות

מתי לא להשתמש בתבניות

הקוד הטוב ביותר הוא הקוד הפשוט ביותר שמפתור נכון את הבעיה – לפעמים זהו דפוס עיצובי בעל מיקום טוב, אבל לעתים קרובות זה רק פונקציה וקטייקט.

  • פתרון פשוט יותר יעבוד גם כן
  • אתה משתמש בדפוסים לשם שימוש בדפוסים
  • התבנית מוסיפה מורכבות מיותרת לקוד פשוט
  • הצוות שלך לא מכיר את הדפוס והתיעוד חסר
  • הבעיה לא באמת מתאימה לכוונת הדפוס

לכל דפוס יש את הרכישות שלו, ואתה צריך לשים לב יותר למה אתה בוחר דפוס מסוים מאשר כיצד ליישם אותו.ההחלטה להשתמש דפוס צריך להיות מונע על ידי הבעיה ביד, לא על ידי רצון להפגין ידע של דפוסים.

אנטי-פטרונים להימנע ב- Python

לא כל הדפוסים הגיוניים במערכת האקולוגית של פייתון.המודולים של Python כבר בודדים - כל מודול מיובא רק פעם אחת, כל כך מפורש כיתות רווקות להוסיף מורכבות מיותרת, עם חלופות טובות יותר להיות משתנים ברמה מודולית או הזרקת תלות.

תגיות: לא Fit Python Well

  • (ב) אלוהים ObjectveFLT:1: מרכזי יותר מדי היגיון בכיתה אחת, הופך את הקוד קשה יותר לבדוק ולשמור, עם החלופה הטובה יותר להיות פיצול פונקציונליות לשיעורים קטנים יותר, כפייתיים.
  • [ה]התערות: [ה]: [ה], [ה], [ה], [ה], [ה'], [ה'], [ה'], [ה'], [ה']: "העצים של הירושה עמוקה הופכים את הקוד לערעור, ולכן מעדיפים את ההרכב והמשלחת.
  • (ב) [15] , ⁇ ⁇ ⁇ : ⁇ ה"ד" של פייתון מבטל לעתים קרובות את הצורך בהיררכיה מורכבת של ממשק

שיקולים מודרניים של Python

ב- Python המודרני (3.8+), מעדיפים פרוטוקול לשחיקה מבנית כפי שהוא אינו דורש ירושה מפורשת, עם שיעורים המספקים פרוטוקול רק על ידי שיטות נכונות, תוך שימוש ב- ABC כאשר אתה רוצה לאכוף ירושה ולספק יישום ברירת מחדל.

שימוש בפרוטוקולים עבור Duck Typing

from typing import Protocol

class Drawable(Protocol):
 def draw(self) -> str:
 ...

class Circle:
 def draw(self) -> str:
 return "Drawing a circle"

class Square:
 def draw(self) -> str:
 return "Drawing a square"

def render(shape: Drawable) -> None:
 print(shape.draw())

# Both Circle and Square satisfy the Drawable protocol
# without explicit inheritance
render(Circle())
render(Square())

עיצוב תבניות ב-Pithon Applications

ב-2026, Python יושב בצומת של AI ולמידה מכונה, פיתוח מוצרים בקנה מידה רחב, והנדסת נתונים ארגונית, עם הדומיננטיות שלה לשקף יתרון מבני שצוותי הנדסה גילו לפני שנים: Python מאפשר לך לנוע מהר יותר, להשתלב בקלות רבה יותר, ולבנות מערכות כי הם נשמרים על ידי צוותים, לא רק על ידי המחבר המקורי.

תבניות המסגרת-Specific

Django נשאר המסגרת ה- Python המלאה ביותר עבור צוותים בבניית מוצרים עם גישה מרובה משתמשים, מודלים מורכבים של נתונים, ממשקי ניהול ומערכות אימות. Django משתמשת באופן נרחב בדפוסים כגון:

  • (ב) ויקרא י"א): "הבדלו של דנגו" (ב"ד)
  • (ב) ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇
  • (ב) ויקרא י"א: ⁇ ⁇
  • (ב) ויקרא י"ד: ויקרא י"ד: "הבקשה/התב" (בראשית כ"ד)

FastAPI מנף את התכונות והתבניות המודרניות של Python:

  • (ב) ,0) ,התזרקות של ההרחבה: מערכת DI
  • (ב) ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇
  • (ב) ,0) ,[[1924]]: [[1924]]
  • (ב) ,0) ,4 ; .

דפוסים אדריכליים

בשנת 2026, הקונצנזוס בין צוותי הנדסה של פייתון מנוסים ברור יותר: להתחיל עם מונולית ממונולית מובנת היטב, להיפטר לשירותים כאשר גבולות ספציפיים מופיעים משימוש אמיתי, כפי מונוליטית הוא לא מצב כשל.

Instagram רץ על מדוניג'נגו היטב בעבר 100 מיליון משתמשים לפני שמחק פונקציות מטען ספציפיות לשירותים, עם המפתח לבניית מונוליטית עם פירוק שירות בראש מההתחלה דרך גבולות מודול ברורים, מינימלי טווחי הפיכה חוצה בינוני, ודפוסי תקשורת מונחה אירועים.

בדיקות עיצוב

תבניות עיצוב צריכות להפוך את הקוד ליותר במבחן, לא פחות.כאן אסטרטגיות לביצועים של דפוסים:

מבחן אסטרטגיה

import unittest
from unittest.mock import Mock

class TestPaymentStrategies(unittest.TestCase):
 def test_credit_card_payment(self):
 strategy = CreditCardPayment("1234567890123456", "123", "12/25")
 result = strategy.pay(100.00)
 self.assertIn("Credit Card", result)
 self.assertIn("100.00", result)

 def test_paypal_payment(self):
 strategy = PayPalPayment("[email protected]")
 result = strategy.pay(50.00)
 self.assertIn("PayPal", result)
 self.assertIn("[email protected]", result)

 def test_shopping_cart_with_different_strategies(self):
 cart = ShoppingCart()
 cart.add_item("Item 1", 50.00)

 # Test with credit card
 cart.set_payment_strategy(CreditCardPayment("1234", "123", "12/25"))
 result1 = cart.checkout()

 # Test with PayPal
 cart.set_payment_strategy(PayPalPayment("[email protected]"))
 result2 = cart.checkout()

 self.assertIsNotNone(result1)
 self.assertIsNotNone(result2)

בדיקה ב-Colle Pattern

class TestObserverPattern(unittest.TestCase):
 def test_observer_notification(self):
 stock = StockPrice("AAPL", 150.00)
 observer = Mock(spec=Observer)

 stock.attach(observer)
 stock.price = 155.00

 observer.update.assert_called_once_with(stock)

 def test_multiple_observers(self):
 stock = StockPrice("GOOGL", 2800.00)
 observer1 = Mock(spec=Observer)
 observer2 = Mock(spec=Observer)

 stock.attach(observer1)
 stock.attach(observer2)
 stock.price = 2850.00

 observer1.update.assert_called_once()
 observer2.update.assert_called_once()

 def test_detach_observer(self):
 stock = StockPrice("MSFT", 300.00)
 observer = Mock(spec=Observer)

 stock.attach(observer)
 stock.detach(observer)
 stock.price = 310.00

 observer.update.assert_not_called()

שיקולים

בעוד תבניות עיצוב משפרות את ארגון הקוד ואת יכולת המשיכה, הם יכולים להציג ביצועים מעל פני אם לא ייושמו בזהירות.

Lazy Preization

הדוגמה נוצרת רק כאשר שיטת ה-Get instance (ה) נקראת לראשונה, ולהבטיח שהמשאבים יוקצו רק כאשר יש צורך.זה חשוב במיוחד עבור אובייקטים בעלי עוצמה:

class DatabaseConnection:
 _instance = None
 _initialized = False

 def __new__(cls):
 if cls._instance is None:
 cls._instance = super().__new__(cls)
 return cls._instance

 def __init__(self):
 if not DatabaseConnection._initialized:
 self._connect()
 DatabaseConnection._initialized = True

 def _connect(self):
 # Expensive connection operation
 print("Establishing database connection...")
 self.connection = "Connected"

תוצאות דקורטיביות Caching

from functools import lru_cache

@lru_cache(maxsize=128)
def expensive_computation(n):
 """Cached using built-in LRU cache"""
 print(f"Computing for {n}...")
 return sum(range(n))

# First call computes
result1 = expensive_computation(1000)

# Second call returns cached result
result2 = expensive_computation(1000)

Best Practices for Design Patterns in Python

דפוסי עיצוב צריכים לפשט, לא לסבך, שכן דפוסי עיצוב פייתון אינם על העתקת דיאגרמות ספרי לימוד - הם עומדים לפתור בעיות אמיתיות אלגנטיות.עקוב אחר הנחיות אלה ליישום דפוס יעיל:

1.הרכב הבוגר מעל ההגינות

הטבע הדינמי של פייתון הופך את ההרכב לעוצמתי במיוחד במקום היררכיה עמוקה של הירושה, ויוצר אובייקטים ממרכיבים קטנים וממוקדים:

# Instead of inheritance
class EmailNotifier:
 def send(self, message):
 print(f"Sending email: {message}")

class SMSNotifier:
 def send(self, message):
 print(f"Sending SMS: {message}")

# Use composition
class NotificationService:
 def __init__(self):
 self.notifiers = []

 def add_notifier(self, notifier):
 self.notifiers.append(notifier)

 def notify(self, message):
 for notifier in self.notifiers:
 notifier.send(message)

# Usage
service = NotificationService()
service.add_notifier(EmailNotifier())
service.add_notifier(SMSNotifier())
service.notify("Important update!")

השתמש בסוג הינדים לקלייריות

רמזים מסוג הופכים את יישום דפוס למפורט יותר ומאפשרים תמיכה טובה יותר ב- IDE:

from typing import Protocol, List
from abc import abstractmethod

class PaymentMethod(Protocol):
 def process(self, amount: float) -> bool:
 ...

class PaymentProcessor:
 def __init__(self, methods: List[PaymentMethod]):
 self.methods = methods

 def charge(self, amount: float) -> bool:
 for method in self.methods:
 if method.process(amount):
 return True
 return False

3. מסמך תבנית

תמיד לתעד איזה דפוס אתה משתמש ומדוע:

class DataExporter:
 """
 Uses the Strategy pattern to support multiple export formats.

 This allows adding new export formats without modifying existing code,
 following the Open/Closed Principle.

 Example:
 exporter = DataExporter(JSONExportStrategy())
 exporter.export(data)
 """

 def __init__(self, strategy: ExportStrategy):
 self._strategy = strategy

 def export(self, data):
 return self._strategy.export(data)

4.המשך זה פשוט

אל תפעילו יתר על המידה, התחילו פשוט ומספקים דפוסים כאשר המורכבות מצדיקה זאת:

# Simple is often better
def calculate_discount(price, customer_type):
 discounts = {
 'regular': 0,
 'premium': 0.10,
 'vip': 0.20
 }
 return price * (1 - discounts.get(customer_type, 0))

# Only introduce Strategy pattern when you need:
# - Runtime strategy switching
# - Complex discount logic
# - Multiple discount calculation methods

מקורות ל- Learning Design Patterns

כדי להעמיק את ההבנה של תבניות עיצוב ב Python, לחקור את המשאבים החשובים האלה:

  • (ב) [15] ,[[1924]]]]]]]], [[1924]]]]]]]], [[1924]]]]]]]]
  • (ב) ויקרא י"א: ויקרא י"ד: ויקרא י"ד: ויקרא י"ד:2 ויקרא יט: ויקרא יט:
  • (ב) [15] ,[[1924]]]]]]]]]], [[1924]]]]]]]]]]]]
  • (ב) ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇
  • (ב) [ה]: [ה] [ה]] [ה]]: [ה] [ה] [ה]] [ה]]][ה]]][2] ,364 ‭ ‬ה[[1924]]]]], ו[[1924]]]]

מסקנה

תבניות עיצוב הן כלים חזקים ערכת הכלים של מהנדס פייתון, אבל הם צריכים להיות מיושם באופן עסיסי. אסטרטגיה, מפעל, ודפוסי Observer מופיעים לעתים קרובות יחד במערכות פייתון , עם אסטרטגיה המתמקדת בבחירת אלגוריתמים חילופי, מפעלים ריכוז על איך חפצים נוצרים, ו- Observer מטפל תקשורת, עם הבנה של הבדלים אלה עוזר לך לבחור את התבנית הנכונה בהתבסס על האתגר שלך הוא על התנהגות, יצירה, תקשורת.

המפתח לשימוש בהצלחה בדפוסי עיצוב ב- Python הוא הבנה הן את הדפוסים עצמם והן את המאפיינים הייחודיים של פייתון.הטיפוס הדינמי של Python, פונקציות ברמה הראשונה, וספרייה סטנדרטית חזקה לעתים קרובות לאפשר יישום פשוט יותר מאשר בשפות סטטיות-טיפוס.

זכור כי דפוסים הם אמצעים לסיום, לא מסתיים בעצמם.המטרה היא לכתוב קוד שקל להבין, לבדוק ולשנות.כאשר דפוס עוזר להשיג את המטרה, להשתמש בו.כאשר פתרון פשוט יותר מספיק, לאמץ פשטות.כפי שאתה מקבל ניסיון, אתה לפתח אינטואיציה עבור כאשר דפוסים מוסיפים ערך וכאשר הם מוסיפים מורכבות מיותרת.

על ידי שליטה בדפוסי עיצוב והבנה כאשר ליישם אותם, אתה תהיה מצויד יותר לבנות יישומי Python חזקים, מדרגיים לעמוד במבחן הזמן ודרישות מתפתחות.