יום רביעי

Singleton

ישנם מצבים בהם נרצה למנוע יצירה של יותר מאובייקט אחד של class נתון. דוגמא: תוכנית לניהול חנות נעליים. יש לי חנות נעליים אחת בלבד. אני מעוניין לצור אובייקט יחיד של החנות ולהשתמש באותו אובייקט בכל חלקי התוכנית. אני מעוניין לחסום אפשרות יצירת יותר מאובייקט אחד, כי יש לי רק חנות אחת!
דרך אחת לבצע זאת היתה יכולה להיות ע"י יצירת אובייקט גלובלי, כך שהוא יהיה מוכר בכל חלקי התוכנית. פתרון זה נוגד את המהות של ה-OOP, בכך שהוא מבטל את עקרונות ה-information hiding וה-data encapsulation. 
הפתרון הנכון הוא ה-singleton.

השיטה של ה-singletone לחסימת האפשרות ליצירת מספר אובייקטים, היא ע"י קביעת ה- access level של ה-constructor לרמה השונה מ-public,  כלומר private או protected. מקווה לפרט יותר על ה-access control בפוסט אחר.
בנוסף, נדאג לכך שה-constructor יופעל פעם אחת בלבד, ובפעמים האחרות לא יווצר object instance חדש, אלא יוחזר מצביע ל-instance הקיים.
ישנה יותר משיטה אחת למימוש singleton. אדגים כאן את אחת השיטות.
הערה: השיטה שתוצג כאן היא הפשוטה והבטוחה יותר, כי היא מבוצעת ללא הקצאת זכרון דינמית.

אז הנה הדוגמא.
מדובר באובייקט המתאר חנות נעליים.
בסיס הנתונים: מלאי החנות, המורכב משלושה משתנים:
1. מלאי הנעליים השחורות.
2. מלאי הנעליים הלבנות.
3. מלאי הנעליים האדומות.

בנוסף, ה-class כולל 3 מתודות:
getInstance - לשליפת המצביע על האובייקט היחידי שקיים ל-class.
sell - מבצעת פעולת מכירה של נעל אחת, ומפחיתה את מלאי הנעליים בהתאם לצבע.
show - מציגה את מלאי הנעליים בכל אחד מהצבעים.


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


נתחיל עם הגדרת ה-class.

  1. /*
  2.  * store.h
  3.  *
  4.  *  Created on: Mar 14, 2012
  5.  *      Author: ronen halevy
  6.  */

  7. #ifndef STORE_H_
  8. #define STORE_H_


  9. class Store{
  10. private:
  11. int num_of_balck_shoes;
  12. int num_of_white_shoes;
  13. int num_of_red_shoes;
  14. Store(int balck_shoes, int white_shoes, int red_shoes);
  15. public:
  16. enum{BLACK = 0, WHITE = 1, RED = 2};
  17. static Store *getInstance();
  18. void sell(int color);
  19. void show(void);
  20. };
  21. #endif /* STORE_H_ */


החלק המודגש בצהוב הוא איזור ה-private והירוק public.
שורות 14-16: בסיס הנתונים של ה-class.
שורה 18: ה-constructor. הוא נמצא באיזור ה-private כך שלא ניתן לצור אובייקטים מבחוץ.
שורה 21:  getInstance. בעזרת המתודה הזו אפשר לשלוף את המצביע על האובייקט היחיד שנוצר.
שורה 20: הגדרת enum, לצורך שיפור קריאות הקוד.


הנה מימוש המתודות של ה-class:
  1. /*
  2.  * store.cpp
  3.  *
  4.  *  Created on: Mar 14, 2012
  5.  *      Author: ronen halevy
  6.  */


  7. #include <iostream>
  8. using namespace std;
  9. #include "store.h"



  10. Store::Store(int balck_shoes, int white_shoes, int red_shoes){
  11. Store::num_of_balck_shoes =  balck_shoes;
  12. Store::num_of_white_shoes =  white_shoes;
  13. num_of_red_shoes = red_shoes;
  14. }

  15. Store *Store::getInstance(){
  16. static Store storeInstance(1,2,3);
  17. return &storeInstance;
  18. }

  19. void Store::sell(int color){
  20. switch(color){
  21. case BLACK:
  22. if(num_of_balck_shoes)
  23. num_of_balck_shoes--;
  24. break;
  25. case WHITE:
  26. if(num_of_white_shoes)
  27. num_of_white_shoes--;
  28. break;
  29. case RED:
  30. if(num_of_red_shoes)
  31. num_of_red_shoes--;
  32. break;
  33. default:
  34. cout << "unknow color";
  35. break;
  36. }

  37. }
  38. void Store::show(void){
  39. cout << "num_of_balck_shoes is " << num_of_balck_shoes << endl << "num_of_white_shoes is " << num_of_white_shoes
  40. << endl << "num_of_red_shoes is " << num_of_red_shoes << endl;

  41. }


ה-class כולל 4 מתודות, אבל נתעניין בעיקר בשתיים הראשונות.
שורה 15: ה-constructor. מקבל 3 פרמטרים ומאתחל בעזרתם את מלאי הנעליים השחורות, אדומות ולבנות.

שורה 21: זו המתודה הכי מעניינת בפוסט הזה. תפקידה לצור אובייקט יחיד של Store, ולהחזיר מצביע אליו.
שורה 22:
static Store storeInstance(1,2,3);

כאן נוצר אובייקט סטטי מסוג Store. היות שהוא סטטי, הוא יווצר אך ורק פעם אחת - בפעם הראשונה שהמתודה getInstance תופעל. בזמן היצירה שלו, יופעל ה-constructor עם הפרמרים 1,2,3.
שורה 23: הערך שמחזירה המתודה הוא מצביע לאובייקט.

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




  1. /*
  2.  * my_store.cpp
  3.  *
  4.  *  Created on: Mar 14, 2012
  5.  *      Author: ronen halevy
  6.  */

  7. #include <iostream>
  8. using namespace std;
  9. #include "store.h""
  10. int main() {
  11.    Store *store1,*store2;
  12. cout << " take singlton's instance " << endl;
  13.    store1 = Store::getInstance();
  14.    store1->show();
  15. cout << " take singlton's instance " << endl;
  16.    store2 = Store::getInstance();
  17.    store2->sell(store1->BLACK);
  18.    store1->show();
  19. }





המתודה מדגימה שליפה של ה-instance היחיד של האובייקט מסוג Store. היא משתמשת ב-3 מתודות של ה-class שהן:
getInstance - מביאה מצביע של ה-instance היחיד.
show- מציגה את בסיס הנתונים של האובייקט = מלאי הנעליים מכל צבע.
sell.

בקטע הקוד הצהוב נשלפת  כתובת ה-instance למצביע store1, ובקטע הקוד הירוק כתובת ה-instance נשלפת לתוך store2. עבור כל אחד מה-instances הנ"ל, מפעילים את show שמציגה את ה-dbs של האובייקט.
פעולת sell שהתבצעה עם store2 בשורה 18, הקטינה את כמות הנעליים השחורות שמוצגת עם store1 (שורה 19). הוכחנו שזה singleton!
הנה הפלט:

 take singlton's instance
num_of_balck_shoes is 1
num_of_white_shoes is 2
num_of_red_shoes is 3
 take singlton's instance
num_of_balck_shoes is 0
num_of_white_shoes is 2
num_of_red_shoes is 3



קבצים להורדה:


2 תגובות: