Menu
Coddy logo textTech
flag Ar iconالعربيةdown icon

نمط 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.

challenge icon

التحدي

سهل

في هذا التحدي، ستعمل على تطبيق النمط التصميمي 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")
quiz iconاختبر نفسك

يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.

جميع دروس Object Oriented Programming