استاتیک در مقابل سینگلتون
- کلاس Static به طور ضمنی انتزاعی است، به این معنی که یک شی کلاس ایستا نه میتواند ایجاد شود و نه نمونهسازی شود، در حالی که الگوی singleton به ما اجازه میدهد یک نمونه واحد (فقط یک) از یک کلاس ایجاد کنیم.
- کلاس استاتیک یک کلاس بدون حالت است در حالی که کلاس سینگلتون حالت کامل دارد
- نمونه کلاس ایستا هر زمان که برای اولین بار به آن دسترسی پیدا کرد ایجاد می شود و در سراسر برنامه یا هر دامنه در دسترس قرار می گیرد، در حالی که الگوی Singleton انعطاف پذیری را فراهم می کند که به ما امکان می دهد نمونه سازی را بر اساس شرایط کنترل کنیم. برای مثال، نمونهای از کلاس singleton را میتوان در هر زمینه ایجاد کرد، مانند نمونهای از ShoppingCart که میتوان برای هر مشتری ایجاد کرد.
- دادههای ذخیرهشده در متغیر یا کلاس استاتیک بهصورت سراسری در سراسر برنامه به اشتراک گذاشته میشوند و در دسترس هستند، در حالی که نمونه singleton ایجاد شده در هر زمینه به اشتراک گذاشته میشود و در هر زمینه قابل دسترسی است، به این معنی که دادههای ذخیره شده با نمونه ایجاد شده در زمینه، به اشتراک گذاشته نمیشوند یا از نمونه ایجاد شده در زمینه دیگر قابل دسترسی نیستند. به عنوان مثال موارد ذخیره شده در سبد خرید در بین جلسات مختلف ورود به سیستم یک مشتری به اشتراک گذاشته می شود و قابل دسترسی است اما برای مشتری دیگر قابل دسترسی نخواهد بود.
- کلاس استاتیک را نمی توان از هیچ کلاس دیگری به ارث برد یا مشتق کرد، در حالی که singleton می تواند رابط ها را پیاده سازی کند و از کلاس دیگری ارث بری کند.
- کلاس استاتیک را نمی توان به عنوان پارامتر به هیچ روشی ارسال کرد در حالی که نمونه کلاس singleton را می توان به عنوان پارامتر به متد یا سازنده کلاس دیگر ارسال کرد.
- فقط اعضای/فیلدها/ویژگی های ایستا را می توان در کلاس استاتیک تعریف کرد، در حالی که کلاس singleton هر دو عضو/فیلد/خواص ایستا و غیر ایستا را می دهد.
ما اکنون پیاده سازی موارد استفاده ذکر شده در زیر را که تمام حقایق فوق را نشان می دهد، بررسی و درک خواهیم کرد.
استفاده از مورد
بیایید به پورتال اینترنتی تجارت الکترونیکی بیندیشیم که هزاران مشتری قبلاً در آن مشترک شده اند. ممکن است سناریویی وجود داشته باشد که چند صد نفر به طور همزمان وارد شوند که از بین آنها چند نفر از چندین برنامه وارد پورتال وب می شوند. به عنوان مثال، پورتال وب، برنامه تلفن همراه یا فرآیندهای پسزمینه، مشتریانی که وارد سیستم میشوند، موارد را جستجو کرده و به سبد خرید خود اضافه میکنند. اکنون به مشتریانی فکر کنید که از چندین برنامه وارد شده اند و از چندین برنامه اقلامی را به سبد خرید اضافه کرده اند.
اکنون درخواست ارائه راه حلی است که هر مشتری باید بتواند تمام اقلام سبد خرید شخصی خود را مشاهده کند اما یک کاربر خاص بتواند تمام موارد اضافه شده در سبد خرید را از برنامه های مختلف مشاهده کند.
راه حل
اول از همه ما راه حلی را برای داشتن نمونه های مختلف سبد خرید به ازای هر مشتری پیاده سازی می کنیم، بنابراین به سه موجودیت در راه حل نیاز داریم که در زیر ذکر شده است:
- کلاس ShoppingCart که نشان دهنده سبد خرید است. این کلاس دارای یک فیلد خصوصی برای ذخیره اقلام اضافه شده به سبد خرید است.
- طبقه مشتری که نماینده یک مشتری است.
- کلاس LoggedinCustomers که لیستی از مشتریان وارد شده را ذخیره می کند
آیا امکان ایجاد نمونه سبد خرید برای هر مشتری با اجرای کلاس ایستا وجود دارد؟ پاسخ خیر است زیرا کلاس استاتیک به طور ضمنی انتزاعی است به این معنی که شی کلاس Static نه می تواند ایجاد شود و نه نمونه سازی شود. Net CLR به طور ضمنی یک نمونه از کلاس ایستا را هر زمان که برای اولین بار به آن دسترسی پیدا کرد به صورت داخلی ایجاد می کند و همان نمونه در بین تمام اشیاء دیگر در یک برنامه به اشتراک گذاشته می شود.
حال این سوال پیش میآید که آیا میتوان با اجرای الگوی تکتنه، نمونهای از سبد خرید برای هر مشتری ایجاد کرد؟ پاسخ بله است، ما میتوانیم از الگوی Singleton استفاده یا پیادهسازی کنیم تا یک نمونه ایجاد شده به ازای هر مشتری را محدود کنیم، به این معنی که نمونه سبد خریدی که برای مشتری یک ایجاد میشود با نمونه ایجاد شده برای مشتری دو متفاوت است. سپس همان نمونه سبد خرید را می توان به جلسات مختلف ورود مشتری منتقل کرد.
بیایید مراحل ذکر شده زیر را برای اجرای این پیاده سازی دنبال کنیم،
- یک الگوی Singleton را در کلاس ShoppingCart اجرا کنید که اطمینان یا محدود می کند که تنها یک نمونه از سبد خرید ایجاد شده است.
- برای برآورده کردن نیاز ما به یک نمونه به ازای هر مشتری، یک پرچم Boolean اضافه کنید تا مشخص شود که آیا ما باید یک نمونه از ShoppingCart ایجاد کنیم یا خیر.
- یک متغیر شی مجموعه از نوع List را برای ذخیره اقلام اضافه شده به سبد خرید اعلام کنید.
- /// <summary>
- /// An abstract representation of Shopping Cart.
- /// This class is the implementation of singleton design pattern
- /// which restricts the instantiation of a class to one object
- /// </summary>
- public sealed class ShoppingCart
- {
- private static volatile ShoppingCart _instance; //A static variable which holds a reference to the single created instance
- private static readonly object SyncRoot = new Object();
- private readonly List<string> _itemsAdded = new List<string>();
- /// <summary>
- /// private and parameterless constructor that prevents other classes from instantiating it
- /// </summary>
- private ShoppingCart() { }
- /// <summary>
- /// This method is to get the instance of ShoppingCart class
- /// </summary>
- /// <param name="bCreateInstance">This flag determines whether new instance is to be created or not</param>
- /// <returns>An instance of ShoppingCart class</returns>
- public static ShoppingCart Instance(bool bCreateInstance)
- {
- if ((_instance == null) || (bCreateInstance == true))
- {
- lock (SyncRoot)
- {
- if ((_instance == null) || (bCreateInstance == true))
- _instance = new ShoppingCart();
- }
- }
- return _instance;
- }
- /// <summary>
- /// This method is to add item into the Shopping cart
- /// </summary>
- /// <param name="itemCode"></param>
- public void AddItemToShoppingCart(string itemCode)
- {
- _itemsAdded.Add(itemCode);
- }
- /// <summary>
- /// This method is to get the list of items added into the shopping cart
- /// </summary>
- /// <returns>A list of item added into the shopping cart</returns>
- public List<string> GetShoppingCartItems()
- {
- return _itemsAdded;
- }
- }
- اکنون یک کلاس Customer با یک فیلد برای ذخیره یک نمونه از ShoppingCart تعریف کنید.
- در سازنده کلاس مشتری یک نمونه از کلاس ShoppingCart ایجاد کنید.
- در کلاس Customer یک روش "AddItemToShoppingCart" را برای افزودن کالا به سبد خرید و روش "PrintShoppingCartItem" را برای چاپ همه مواردی که در حال حاضر به سبد خرید اضافه شده است، تعریف کنید.
- /// <summary>
- /// This class is a blueprint of a customer
- /// </summary>
- public class Customer
- {
- //A local variable to store an instance of ShoppingCart.
- private readonly ShoppingCart _shoppingCartInstance = null;
- #region Properties
- /// <summary>
- /// Property to hold customer name
- /// </summary>
- public string CustomerName { get; set; }
- #endregion
- #region Constructor
- /// <summary>
- /// Constructor to initialize Customer Identifier and Customer Name
- /// </summary>
- /// <param name="customerName">Customer Name</param>
- ///
- public Customer(string customerName)
- {
- CustomerName = customerName;
- _shoppingCartInstance = ShoppingCart.Instance(true);
- }
- #endregion
- /// <summary>
- /// This method is to the add item into the Shopping Cart
- /// </summary>
- /// <param name="itemCode">Item to be added into the shopping cart</param>
- public void AddItemToShoppingCart(string itemCode)
- {
- _shoppingCartInstance.AddItemToShoppingCart(itemCode);
- }
- /// <summary>
- /// This method is to print the items currently added into the shopping cart
- /// </summary>
- public void PrintShoppingCartItem()
- {
- List<string> itemsAdded = _shoppingCartInstance.GetShoppingCartItems();
- StringBuilder itemsAddedToShoppingCart = new StringBuilder();
- foreach (var item in itemsAdded)
- {
- if (itemsAddedToShoppingCart.Length > 0)
- itemsAddedToShoppingCart.Append(", ");
- itemsAddedToShoppingCart.Append(item);
- }
- Console.WriteLine("Items Added in {0}'s Cart : {1}", CustomerName, itemsAddedToShoppingCart.ToString());
- }
- }
- حال یک کلاس به نام LoggedinCustomers تعریف کنید.
- یک فیلد "_customers" را در کلاس LoggedinCustomers تعریف کنید که شامل مجموعه ای از اشیاء ثبت شده مشتری است. روشی به نام «AddCustomer» برای افزودن مشتری به فیلد شی مجموعه تعریف کرد. برای به دست آوردن لیستی از اشیاء مشتری ذخیره شده در شی مجموعه، روشی به نام "" تعریف کرد
- public class LoggedinCustomers
- {
- private readonly List<Customer> _customers = new List<Customer>();
- /// <summary>
- /// This method is to get the customer from loggedin customer collection based on the customer name
- /// </summary>
- /// <param name="customerName">Customer Name</param>
- /// <returns>An instance of customer</returns>
- public Customer GetCustomer(string customerName)
- {
- Customer customer = _customers.FirstOrDefault(x => x.CustomerName == customerName);
- return customer;
- }
- /// <summary>
- /// This method is to add customer into the loggedin customer collection
- /// </summary>
- /// <param name="customer">an instance of customer class</param>
- public void AddCustomer(Customer customer)
- {
- _customers?.Add(customer);
- }
- /// <summary>
- ///
- /// </summary>
- /// <returns></returns>
- public List<Customer> GetCustomers()
- {
- return _customers;
- }
- }
اکنون برنامهای بنویسید که نمونهای از کلاس مشتری را برای کاربران مختلف نمونهسازی کند، در حالی که نمونهای از کلاس مشتری موجود را از فهرستی از مشتریان ثبتشده نگهداری شده بازیابی میکند. به این ترتیب ما می توانیم نمونه های مختلفی از سبد خرید را برای هر مشتری ایجاد کنیم و کالا را به سبد خرید اضافه کنیم.
قطعه کد
- public static void StartProcess()
- {
- LoggedinCustomers loggedinCustomers = new LoggedinCustomers();
- while (true)
- {
- Console.Clear();
- PrintLoggedinCustomers(loggedinCustomers);
- Console.Write("Enter 1 to login or 0 to logout from the application: ");
- string input = Console.ReadLine();
- if (input == "1")
- {
- var currentCustomer = LoginCustomer(loggedinCustomers);
- Console.WriteLine("{0} logged in sucessfully!! Let's Start Shopping ", currentCustomer.CustomerName);
- StartShopping(currentCustomer);
- }
- else
- {
- break;
- }
- }
- }
- private static Customer LoginCustomer(LoggedinCustomers loggedinCustomers)
- {
- Console.Write("Enter Customer Name to login: ");
- string customerName = Console.ReadLine();
- //search if the entered customer is already logged in then use the object
- var currentCustomer = loggedinCustomers.GetCustomer(customerName);
- //If the entered customer is not loggedin already then create a new instance
- if (null == currentCustomer)
- {
- currentCustomer = new Customer(customerName);
- loggedinCustomers.AddCustomer(currentCustomer);
- }
- return currentCustomer;
- }
- private static void PrintLoggedinCustomers(LoggedinCustomers loggedinCustomers)
- {
- var customers = loggedinCustomers.GetCustomers();
- if (0 >= customers.Count)
- {
- Console.WriteLine("No Customer is currently logged in to the application");
- }
- else
- {
- foreach (Customer customer in customers)
- {
- Console.WriteLine("{0} is currently logged in", customer.CustomerName);
- customer.PrintShoppingCartItem();
- }
- }
- }
- private static void StartShopping(Customer customer)
- {
- Console.WriteLine("Enter 0 to logout from the application");
- Console.WriteLine("Enter 1 to Print Shopping Cart Item");
- Console.WriteLine("Enter 2 to add item into the shopping cart");
- string line;
- while ((line = Console.ReadLine()) != "0")
- {
- if (line == "1")
- {
- customer.PrintShoppingCartItem();
- }
- else if (line == "2")
- {
- Console.WriteLine("Enter 9 to Stop Shopping else add Item Name to Add into the shopping cart else");
- Console.WriteLine("");
- string itemCode;
- while ((itemCode = Console.ReadLine()) != "9")
- {
- customer.AddItemToShoppingCart(itemCode);
- }
- Console.WriteLine("Enter 0 to logout from the application");
- Console.WriteLine("Enter 1 to Print Shopping Cart Item");
- Console.WriteLine("Enter 2 to add item into the shopping cart");
- }
- }
- }
خروجی
دو مشتری به نامهای «User1» و «User2» وارد برنامه شدند. هر دوی آنها اقلام مختلفی را به سبد خرید اضافه کرده اند و ما می توانیم سبد خرید آنها را در تصویر زیر مشاهده کنیم.
اکنون ما تغییراتی را برای به اشتراک گذاشتن یک نمونه برای تمام جلسه ورود به سیستم یک مشتری ایجاد می کنیم. برای این کار ما به کلاس CustomerLoginSession نیاز داریم که یک جلسه ورود به سیستم را برای یک مشتری نشان می دهد. یک مشتری می تواند از پلتفرم ها یا برنامه های مختلف به عنوان مثال، برنامه وب یا برنامه تلفن همراه وارد شود
بیایید مراحل ذکر شده زیر را برای اجرای این پیاده سازی دنبال کنیم:
- کلاسی به نام «CustomerLoginSession» برای نمایش جلسه ورود مشتری تعریف کنید. این کلاس دارای یک فیلد به نام "_shoppingCartInstance" از نوع ShoppingCart برای نگهداری یک مرجع از نمونه سبد خرید ایجاد شده در کلاس مشتری خواهد بود. کلاس مشتری یک شی را به عنوان پارامتر به سازنده کلاس CustomerLoginSession ارسال می کند.
- در کلاس CustomerLoginSession یک روش "AddItemToShoppingCart" برای افزودن آیتم به سبد خرید تعریف کنید.
- public class CustomerLoginSession
- {
- // An instance of ShoppingCart
- private readonly ShoppingCart _shoppingCartInstance = null;
- private readonly string _customerName;
- public string ApplicationId { get; set; }
- /// <summary>
- /// Default Constructor to initialize Customer Session
- /// </summary>
- /// <param name="instance">An instance of singleton class which is passed as a parameter</param>
- /// <param name="customerLoginApplicationId">Unique Login Session Identifier</param>
- /// <param name="customerName">Customer Name</param>
- public CustomerLoginSession(ShoppingCart instance, string customerLoginApplicationId, string customerName)
- {
- _shoppingCartInstance = instance;
- ApplicationId = customerLoginApplicationId;
- _customerName = customerName;
- }
- /// <summary>
- /// This method is to the add item into the Shopping Cart
- /// </summary>
- /// <param name="itemCode">Item to be added into the shopping cart</param>
- public void AddItemToShoppingCart(string itemCode)
- {
- _shoppingCartInstance.AddItemToShoppingCart(itemCode);
- Console.WriteLine("{0} added into {1} Cart from {2} ", itemCode, _customerName, ApplicationId);
- }
- }
- یک فیلد "_customerLoginSessions" را در کلاس Customer تعریف کنید که شامل مجموعه ای از اشیاء CustomerLoginSession است، زیرا مشتری می تواند چندین جلسه ورود داشته باشد (تعدد 1- به چند *).
- //A list of customer login sessions
- private readonly List<CustomerLoginSession> _customerLoginSessions = new List<CustomerLoginSession>();
- یک ویژگی به نام "CurrentCustomerLoginSession" از نوع CustomerLoginSession در کلاس Customer برای ذخیره جلسه ورود فعلی تعریف کنید. در اجرای واقعی ما به این ویژگی نیاز نداریم. من برای ساده نگه داشتن این پروژه ایجاد کرده ام.
- private CustomerLoginSession CurrentCustomerLoginSession { get; set; }
- روش «AddItemToShoppingCart» را در کلاس Customer بهروزرسانی کنید تا روش «AddItemToShoppingCart» را به شی CurrentCustomerLoginSession فراخوانی کنید تا موردی را به سبد خرید اضافه کنید.
- public void AddItemToShoppingCart(string itemCode)
- {
- CurrentCustomerLoginSession.AddItemToShoppingCart(itemCode);
- }
- روش "InitializeCustomerLoginSession" را در کلاس Customer اضافه کنید تا شی CustomerLoginSession را نمونه سازی کنید و یک مرجع از نمونه سبد خرید ایجاد شده در کلاس مشتری را ارسال کنید تا هر جلسه ورود به سیستم مشتری به نمونه سبد خرید یکسان دسترسی داشته باشد.
- public void InitializeCustomerLoginSession(string applicationId)
- {
- CustomerLoginSession loginSession = _customerLoginSessions.FirstOrDefault(x => x.ApplicationId == applicationId);
- if (null == loginSession)
- {
- loginSession = new CustomerLoginSession(_shoppingCartInstance, applicationId, CustomerName);
- _customerLoginSessions.Add(loginSession);
- }
- CurrentCustomerLoginSession = loginSession;
- }
اکنون برنامهای بنویسید که نمونهای از کلاس مشتری را برای مشتریان مختلف نمونهسازی میکند، در حالی که نمونهای از کلاس مشتری موجود را از فهرستی از مشتریان ثبتشده نگهداری شده بازیابی میکند، اما جلسه ورود مشتری را برای پلتفرمهای مختلف اولیه میکند. به این ترتیب ما میتوانیم نمونههای مختلفی از سبد خرید را برای هر مشتری ایجاد کنیم، اما نمونه مشابهی از سبد خرید را در بین جلسات مختلف ورود به سیستم مشتری به اشتراک بگذاریم. مشتریان می توانند از جلسات مختلف اقلامی را به سبد خرید اضافه کنند و می توانند تمام موارد اضافه شده از جلسات مختلف را ببینند.
- public static void StartProcess()
- {
- LoggedinCustomers loggedinCustomers = new LoggedinCustomers();
- while (true)
- {
- Console.Clear();
- PrintLoggedinCustomers(loggedinCustomers);
- Console.Write("Enter 1 to login or 0 to exit: ");
- string input = Console.ReadLine();
- if (input == "1")
- {
- var currentCustomer = LoginCustomer(loggedinCustomers);
- StartShopping(currentCustomer);
- }
- else
- {
- break;
- }
- }
- }
- private static Customer LoginCustomer(LoggedinCustomers loggedinCustomers)
- {
- Console.Write("Enter Customer Name to login: ");
- string customerName = Console.ReadLine();
- Console.Write("Enter Application Name Like Web Or Mobile: ");
- string application = Console.ReadLine();
- //search if the entered customer is already logged in then use the object
- var currentCustomer = loggedinCustomers.GetCustomer(customerName);
- //If the entered customer is not loggedin already then create a new instance
- if (null == currentCustomer)
- {
- currentCustomer = new Customer(customerName);
- loggedinCustomers.AddCustomer(currentCustomer);
- }
- currentCustomer.InitializeCustomerLoginSession(application);
- Console.WriteLine("{0} logged in sucessfully from {1} !! Let's Start Shopping ", currentCustomer.CustomerName, application);
- return currentCustomer;
- }
- private static void StartShopping(Customer customer)
- {
- Console.WriteLine("Enter 0 to logout from the application");
- Console.WriteLine("Enter 1 to Print Shopping Cart Item");
- Console.WriteLine("Enter 2 to add item into the shopping cart");
- string line;
- while ((line = Console.ReadLine()) != "0")
- {
- if (line == "1")
- {
- customer.PrintShoppingCartItem();
- }
- else if (line == "2")
- {
- Console.WriteLine("Enter 9 to Stop Shopping else add Item Name to Add into the shopping cart else");
- Console.WriteLine("");
- string itemCode;
- while ((itemCode = Console.ReadLine()) != "9")
- {
- customer.AddItemToShoppingCart(itemCode);
- }
- Console.WriteLine("Enter 0 to logout from the application");
- Console.WriteLine("Enter 1 to Print Shopping Cart Item");
- Console.WriteLine("Enter 2 to add item into the shopping cart");
- }
- }
- }
- private static void PrintLoggedinCustomers(LoggedinCustomers loggedinCustomers)
- {
- var customers = loggedinCustomers.GetCustomers();
- if (0 >= customers.Count)
- {
- Console.WriteLine("No Customer is currently logged in to the application");
- }
- else
- {
- foreach (Customer customer in customers)
- {
- List<CustomerLoginSession> loginSessions = customer.GetCustomerLoginSessions();
- string applications = string.Join(", ", from item in loginSessions select item.ApplicationId);
- Console.WriteLine("{0} is currently logged in from {1}", customer.CustomerName, applications);
- customer.PrintShoppingCartItem();
- }
- }
- }
مشتری خروجی
به نام "کاربر 1" به طور همزمان از برنامه وب و موبایل وارد برنامه شد. کاربر موبایل، کفش را از برنامه وب به سبد خرید اضافه کرد در حالی که کاربر شلوار جین را از برنامه موبایل اضافه کرد. در نهایت، کاربر می تواند تمام موارد اضافه شده به سبد خرید را بدون توجه به اینکه کدام کالا از کدام برنامه یا جلسه ورود به سیستم اضافه شده است، مشاهده کند.
![خروجی](http://pezhvak24.ir/dl/10kcor/cscd/article/learn-about-static-vs-singleton/Images/image002.jpg)
ساختار کلی کلاس
![خروجی](http://pezhvak24.ir/dl/10kcor/cscd/article/learn-about-static-vs-singleton/Images/image003.png)
نتیجه
بر اساس مثال بالا، من پیشنهاد می کنم هر زمان که نیاز به ایجاد نمونه بر اساس زمینه ای داشتیم از Singleton روی Static استفاده کنیم و هر زمان که به همان نمونه یا داده در برنامه نیاز داشتیم از Static روی Singleton استفاده کنیم.