תבנית Singleton
חלק מהיחידה תכנות מונחה עצמים במסלול ה-Python של Coddy. שיעור 45 מתוך 64.
תבנית Singleton מבטיחה שלמחלקה יש מופע אחד בלבד ומספקת נקודת גישה גלובלית אליו. היא שימושית למשאבים כמו חיבורי מסד נתונים או הגדרות תצורה.
הנה מימוש בסיסי של סינגלטון:
class Singleton:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instanceהמתודה __new__ שולטת ביצירת אובייקטים. היא בודקת אם מופע כבר קיים לפני יצירת מופע חדש.
צרו שני מופעים של המחלקה Singleton:
singleton1 = Singleton()
singleton2 = Singleton()בדקו אם שני המשתנים מפנים לאותו אובייקט:
print(singleton1 is singleton2) # True
print(id(singleton1)) # אותה כתובת זיכרון
print(id(singleton2)) # אותה כתובת זיכרוןהנה דוגמה מעשית יותר עם חיבור למסד נתונים:
class DatabaseConnection:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
cls._instance.connection = "Connected to MySQL database"
print("Creating new database connection")
return cls._instance
def query(self, sql):
return f"Executing: {sql}"
# הגישה הראשונה יוצרת את החיבור
db1 = DatabaseConnection()
print(db1.connection)
# הגישה השנייה משתמשת שוב באותו חיבור
db2 = DatabaseConnection()
print(db2.connection)
print(db1.query("SELECT * FROM users"))
print(db1 is db2)צרו מנהל תצורה באמצעות Singleton:
class Config:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
cls._instance.settings = {}
return cls._instance
def set_setting(self, key, value):
self.settings[key] = value
def get_setting(self, key):
return self.settings.get(key)
config1 = Config()
config1.set_setting("debug", True)
config2 = Config()
print(config2.get_setting("debug")) # True - אותן הגדרותפלט:
True
140234567890123
140234567890123
Creating new database connection
Connected to MySQL database
Connected to MySQL database
Executing: SELECT * FROM users
True
Trueנקודה מרכזית: תבנית Singleton משתמשת ב-__new__ כדי לשלוט ביצירת אובייקטים, וכך מבטיחה שקיים מופע אחד בלבד. השתמשו בה למשאבים שצריכים להיות קיימים בעותק אחד בלבד לאורך כל היישום שלכם, כמו חיבורי מסד נתונים, רכיבי רישום או מנהלי תצורה. זכרו שכל המשתנים שמצביעים על Singleton מפנים לאותו אובייקט בזיכרון.
אתגר
קלבאתגר הזה תממש את תבנית העיצוב Singleton עבור מחלקת חיבור למסד נתונים.
- ערוך את
databaseconnection.pyכדי לממש את תבנית Singleton בהתאם להערות TODO - הקובץ
driver.pyמכיל תרחישי בדיקה נרחבים ואין לשנות אותו
נסו בעצמכם
from databaseconnection import DatabaseConnection
# מטפל מקיף במקרי בדיקה
test_case = input()
if test_case == "identity_check":
db1 = DatabaseConnection()
db2 = DatabaseConnection()
print(db1 is db2) # אמור להדפיס True
elif test_case == "connect_state":
db = DatabaseConnection()
print(f"Initial connected state: {db.connected}") # אמור להדפיס False
db.connect()
print(f"After connect: {db.connected}") # אמור להדפיס True
elif test_case == "disconnect_state":
db = DatabaseConnection()
db.connect()
db.disconnect()
print(f"After disconnect: {db.connected}") # אמור להדפיס False
elif test_case == "multiple_instances_same_state":
db1 = DatabaseConnection()
db1.connect()
db2 = DatabaseConnection()
print(f"db2 connected state: {db2.connected}") # אמור להדפיס True
elif test_case == "host_value":
db = DatabaseConnection()
print(f"Default host: {db.host}") # אמור להדפיס "localhost"
db.host = "new-server"
db2 = DatabaseConnection()
print(f"New instance host: {db2.host}") # אמור להדפיס "new-server"
elif test_case == "init_once":
db1 = DatabaseConnection()
db1.host = "custom-host"
db2 = DatabaseConnection()
print(f"Both instances have same host: {db1.host == db2.host}") # אמור להדפיס True
print(f"Host value: {db1.host}") # אמור להדפיס "custom-host"
elif test_case == "connect_message":
db = DatabaseConnection()
db.connect() # אמור להדפיס "Connected to database at localhost"
elif test_case == "disconnect_message":
db = DatabaseConnection()
db.connect()
db.disconnect() # אמור להדפיס "Disconnected from database"
elif test_case == "attribute_modification":
db1 = DatabaseConnection()
db1.port = 3306
db2 = DatabaseConnection()
print(f"db2 has port attribute: {hasattr(db2, 'port')}") # אמור להדפיס True
print(f"db2 port value: {db2.port}") # אמור להדפיס 3306
elif test_case == "reset_connection":
db1 = DatabaseConnection()
db1.connect()
print(f"Connected state: {db1.connected}") # אמור להדפיס True
db2 = DatabaseConnection()
print(f"New instance connected state: {db2.connected}") # אמור להדפיס True
db2.disconnect()
db3 = DatabaseConnection()
print(f"After disconnect, new instance state: {db3.connected}") # אמור להדפיס False
elif test_case == "multiple_connects":
db = DatabaseConnection()
db.connect()
db.connect()
db.connect()
print(f"Connected state after multiple connects: {db.connected}") # אמור להדפיס True
db.disconnect()
print(f"Connected state after disconnect: {db.connected}") # אמור להדפיס False
elif test_case == "host_change_affects_message":
db = DatabaseConnection()
db.host = "custom-server"
db.connect() # עדיין ידפיס "Connected to database at localhost"
# למתודה connect יש הודעה שהוגדרה מראש ואינה משתמשת במאפיין host
print("Message still shows localhost: True")השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.
כל השיעורים ביחידה תכנות מונחה עצמים
1יסודות התכנות מונחה העצמים (OOP)
קבצים חיצונייםמבוא ל-OOPמחלקות לעומת אובייקטיםהפרמטר selfמתודותמאפייניםשיטת הבנאי (__init__)חזרה: מחשבון פשוט5פולימורפיזם
בחינה מחדש של דריסת מתודותטיפוסיות ברווזמחלקות ומתודות אבסטרקטיותעיצוב ממשקיםסיכום – מחשבון צורות8מושגים מתקדמים בתכנות מונחה עצמים
קומפוזיציה לעומת הורשהMixinsמתודות סטטיות ומתודות מחלקהדקורטורים למחלקותמנהלי הקשרתרגלו בעצמכם: קומפיילר Python אונליין