نمط Singleton
جزء من قسم Object Oriented Programming في رحلة Python على Coddy — الدرس 45 من 64.
يضمن نمط Singleton أن الـ class يحتوي على instance واحد فقط ويوفر نقطة access عامة إليه. يُعد هذا مفيدًا لموارد مثل اتصالات database أو settings الإعدادات.
إليك تطبيق أساسي لنمط `Singleton`:
class Singleton:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instanceتتحكم الطريقة __new__ في إنشاء الكائنات. فهي تتحقق مما إذا كان هناك instance موجودًا بالفعل قبل إنشاء واحد جديد.
قم بإنشاء أنشطة من الفئة Singleton:
singleton1 = Singleton()
singleton2 = Singleton()تحقق مما إذا كان كلا المتغيرين يشيران إلى نفس الـ object:
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)أنشئ مدير إعدادات (configuration manager) باستخدام نمط النمط المنفرد 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__ للتحكم في إنشاء الـ object، مما يضمن وجود instance واحدة فقط. استخدمه للموارد التي يجب أن تمتلك نسخة واحدة فقط عبر تطبيقك، مثل الـ databaseconnection، أو الـ loggers، أو الـ configuration managers. تذكر أن جميع المتغيرات التي تشير إلى Singleton تشير إلى نفس الـ object في الـ memory.
التحدي
سهلفي هذا التحدي، ستعمل على تطبيق النمط التصميمي 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() # Should print "Connected to database at localhost"
elif test_case == "disconnect_message":
db = DatabaseConnection()
db.connect()
db.disconnect() # Should print "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() # Will still print "Connected to database at localhost"
# تحتوي الدالة connect على رسالة مُرمّزة ثابتة لا تستخدم خاصية host
print("Message still shows localhost: True")يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس Object Oriented Programming
1أساسيات OOP
الملفات الخارجيةمقدمة في OOPClasses مقابل Objectsمعامل selfMethodsAttributesدالة البناء (__init__)مراجعة - آلة حاسبة بسيطة4الوراثة
الوراثة الأساسيةدالة ()superإعادة تعريف الدوال (Method Overriding)الوراثة المتعددةترتيب استدعاء الدوال (Method Resolution Order)مراجعة - هيكلية الموظفين5تعدد الأشكال
مراجعة إعادة تعريف الدوالمفهوم Duck Typingالأصناف والدوال المجردةتصميم الواجهاتملخص - حاسبة الأشكال