Sultan_Althibity

نمط الورقة الوحيدة في السي بلس بلس

6 ردود في هذا الموضوع

<span style='font-size:21pt;line-height:100%'>نمط الورقة الوحيدة

The Singleton Pattern</span>

=====================================

هذا النمط نمط الورقـة الوحيدة أو الـ Singleton (لا أدري إن كانت الترجمـة صحيحـة)، لا يهتـم بكيفية خلق أو صنع الكائنـات بل يهتـم أكثر بأن يجعل هـناك كائن واحد فقط من صنف ما، ولا يهـتم أبداً بكيفية خلق هذا الكائن.

هـناك احتياجات كثيرة لمثل هذا النمط وخاصـة في قواعد البيانات، فحينما تقوم بكتابة قاعـدة بيانات فأنت لن تحتاج سوى لكائن واحد فقط وليس لكائنين أو ثلاثة.

على العـموم ستشير هذه المقالة إلى الكلمـة Singleton بكلمة ورقـة وحيدة ، وبالتالي فسأذكر كائن ورقة وحيدة بدلاً من كائن Singleton .

خلق كائن Singleton بإستخدام الدوال الساكنـة

Creating Singleton Using a Static Method

أفضل طريقـة لتوفير نسخـة واحدة من صنف وحيد هـو بإستخدام الدوال الساكنـة ، فكّر الآن في كيفية منع مستخدم الصنف من إمكانية تخليق عـدة كائنات من الصنف ، الطريقة هي بجعل دالة البناء دالة خاصـة ، وبالتالي فسيستحيل إمكانية صنع أي كائن من الصنف ، وواجبك الآن هـو توفير كائن وحيد فقط من هذا الصنف .

الآن كل ما أريده منك هـو التفكير بهذا الكائن:

1.	class single
2. {
3.  single();
4.  static bool instance_sure;
5. public:
6.  static single* GetSingle();  
7.   };
8.
9. bool single::instance_sure=false;
10.
11. single::single(){}
12.
13. single* single::GetSingle()
14. {
15.  if (!instance_flag){instance_sure=true;
16.  return new single;}
17.  else {return NULL;}
18. }

الكائن Single :

تم تعريف هذا الصنف في الأسطر 1-18 .

أنظر إلى دالة البناء في السطر 3 تجـد أنها دالة خاصـة ، وبالتالي فإن هذا السطر يعتبر خاطيء :

single* a= new single();

والسبب لأنه يقوم باستدعاء دالة البناء من خارج الصنف أو الكائن single .

أنظر إلى المتغير الساكن من النـوع bool وهـو instance_sure ، هذا المتغير يتأكد من عـدد الكائنات التي تم إنشاؤها من الصنف singleton . أنظر إلى السطر 9 لقد تمت تهيئة المتغير instance_sure بالقيمة false .

الآن فإن هذا الصنف لا يسمح لك إلا بإنشاء كائن واحد فقط لا غيـر ، أما عـن طريقة إنشاء هذا الكائن الوحيد ، فهي مثل الموجودة في هذا السطر:

single* a= single::GetSingle();

لاحـظ الدالة GetSingle وطريقة تعريفها في الأسطر من 13 إلى 18 ، تجد أن هذه الدالة تعيد ذاكرة أو مرجعيـة من النـوع single ، وهـي إما أن تكون NULL أو single ، والذي يحدد هذا الشيء هـو المتغير الساكـن instance_sure ، في حال كانت قيمة هذا المتغير تساوي القيمـة false ، فستقوم الدالة GetSingle باختبار هذا المتغير وفي حال كانت قيمتـه false ، فإنها تجعل قيمتـه تساوي true وتقوم بحجز للذاكرة من النـوع single ، في حال أراد المستخدم حجز الذاكرة من جـديد لكائن آخر فستقوم الدالة باختبار المتغير الساكن instance_sure وسيكون في هذه الحالة true وبالتالي فستعيد القيمـة NULL .

الآن يجب عليك التفكير بتطوير ولو صغير لهذا الكائن ، فمن ناحيـة لو قام المستخدم بإلغاء ذاكرة الكائن وإعادة تخصيصها من جـديد ، فإن عليك التفكير في كيفية حل لهذه المشكلـة ، أنظر إلى هذه الأسطر:

1.	single* a= new single();
2. delete a;
3. a=new single();

وعلى العـموم ونظراً لسهـولة حل هذه المشكلة فسأتركها لك كسؤال في آخر شرح هذا النمط التصميمي.

هـناك أيضاً بعض المشاكل أو بشكل أكثر دقـة بعض الإضافات التي يجب عليك إضافتها إلى هذا الصنف ، من ضمن هذه الإضافات ، الاستثناءات ، كيف ستقوم بتنبيه مستخدم الصنف بأنه قام بحجز جـديد للذاكرة ، أنظر إلى التعديلات على الصنف single ليصبح أكثر فعالية ، وحاول أن ترى الإضافات الجـديدة قبل قراءة الأسطر التي تحت الكود:

1.	class single
2. {
3.  single();
4.  static bool instance_flag;
5. public:
6.  static single* GetSingle();
7.  class Exception
8.  {
9.  string base;
10. public:
11.  Exception():base("ThereIsNoInstance"){}
12.  void PrintException(){cout << "ThereIsNoInstance";}
13.  };  
14. };
15. bool single::instance_flag=false;
16.
17. single::single(){}
18.
19. single* single::GetSingle()
20. {
21.  if (!instance_flag){instance_flag=true;
22.  return new single;}
23.  else {throw Exception();return NULL;}
24. }

حتى تفهـم بشكل أوضح أنظر إلى الدالة main والتي سترى فيها كيفية استخدام هذا النمط التصميمي:

1.	int main()
2. {
3.  try{
4.  single* a = single::GetSingle();
5.  single* b = single::GetSingle();
6.  }
7.
8.  catch(single::Exception a)
9.          {
10.  cout << endl; a.PrintException(); cout << endl;
11.  }
12.  return 0;
13. }

أنظر إلى السطر الرابع من الدالة main ، تجد أن مستخدم الصنف سيقوم بحجز للذاكرة دون أن تحدث أية مشاكل.

أنظر إلى السطر الخامس وحاول تتبع تنفيذ البرنامج ، البرنامج سيتوقف عـن العـمل في هذا السطر.

في هذا السطر الخامس سيقوم المستخدم بحجز آخر للذاكرة ، وبما أن قيمـة المتغير instance_sure ، أصبحت true بفعل تنفيذ السطر الرابع ، فلن ينجح حجز الذاكرة ، سينتقل التنفيذ الآن إلى السطر 19 من الصنف single ، في السطر 21 سيتأكد البرنامج إن كانت قيمـة المتغير instance_sure هي false وبما أنها غير ذلك أي true سينتقل التنفيذ إلى الجملة else في السطر 23 والتي ستقوم بإلقاء أحد الاستثناءات وهـو الصنف Exception ، وبالتالي فإن الكتلة catch في السطر 8 من الدالة main ، ستقوم بالتقاطـه وتقوم بتنفيذ نفسها ، وأول أمر ستقوم بـه هـو استدعاء التابع أو الدالة PrintException وهي دالة عضو للصنف Exception ، هذه الدالة لا تقوم بأي شيء سوى بطباعـة الرسالة ThereIsNoInstance ، ومهمتها هي تنبيه المستخدم لعـدم وجود ذاكرة ، بعـد ذلك سينتهي تنفيذ البرنامج تلقائياً.

قد تختلف معـي في ضرورة إنهاء البرنامج حالما يقوم المستخدم بحجز جـديد للذاكرة ، إن السبب في ذلك هـو أن المستخدم ربما سيستخدم الصنف single الجـديد في مهام كثيرة ومتعـددة ، وكما تعلم فإن هذا الكائن لن يكون له أي وجود في البرنامج لأن قيمتـه NULL أو صفر ، وإنهاء البرنامج بطريقة آمنـة أفضل من أن ينتهي بنفسـه وأن يظل مستخدم الصنف يبحث عـن الخطأ دون أن يجد له أي أثر.

تم تعديل بواسطه محمد عودة
ltr
0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

ما شاء الله عليك يا سلطان :( أشكرك على مجهودك وانا أول ما أروح من الكلية هقراء الموضوع بتفصيل

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

بارك الله فيك،، في الوصلة موضوع مشابه في قسم الجافا،، المفهوم مهم،، أتمنى إكمال السلسلة,, بأي لغة كانت!

Singleton بقلم فيصل الردادي

بالتوفيق!

تم تعديل بواسطه أبومازن
0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

شكراً أخي أبو مازن على الرابط .. ولكن الرابط الموجود بلغـة الجافا وليست بلغـة السي بلس بلس

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

ولو أن الموضوع قديم جدا, ولكن عند قرائتي الجديدة له ظهر عندي تساؤل والمشكلة التي طرحتها أنت وسأبدأ بذكرها:

بعد طلب تابع single::GetSingle لحجز ذاكرة للغرض يمكن أن يتم تحريره والآن عند طلب التابع single::GetSingle مرة أخرى لن يقوم بإنشاء الغرض لأن قيمة instance_flag تساوي true

والاستفسار سيتوضح بذكر الصيغة المقترحة لهذا النمط

class single
{
private:
static single *instance;
single();
public:
~single();
static single *getsingle();
};

single *single::instance = NULL;

single::single()
{
instance = this;
};

single::~single()
{
instance = NULL;
};

single *single::getsingle()
{
if ( instance )
{
return instance;
}
else
{
return new single();
}
}

بمعنى أنه بطلب تابع single::GetSingle مرة ثانية يعيد لنا مؤشر على النسخة اللموجودة

وعذرا على تطفلي :D

تم تعديل بواسطه fh_feras
0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

بعد شهور ..فهمت المقصود من هذا الدرس ,, طبعا مو غباء لكن لازم " حقن " :lol:

رأيت في كود بعض الالعاب .. الكلاس المسؤول عن محرك اللعبة .. اللي يدير اللعبة ..ويحجز ذاكرة لبقية الكلاسات .. يكون فقط نسخة وحيدة ..

كنت دائما اشوف كلمة instance في بعض المشاريع .. لم أكن أعرف معناها ..

هذا الدرس كنز ,, ولكن اعتقد أنه أفضل طريقة للتعلم هي الاطلاع على المشاريع ثم قراءة الدروس .. عندها ستقرأ الدرس وانت مقتنع تماما بالفكرة.

جزاك الله خير يا سلطان ,, والله يمتعك بالصحة والعافية .

تم تعديل بواسطه الشمري
0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
زوار
This topic is now closed to further replies.

  • يستعرض القسم حالياً   0 members

    لا يوجد أعضاء مسجلين يشاهدون هذه الصفحة .