الگوهای طراحی به منظور حل مشکلات متداول در شیوه برنامه نویسی شی گرا توسط برنامه نویسان باتجربه ایجاد شدند. این نوع مشکلات مستقل از زبانهای برنامه نویسی هستند. یکی از این پرکاربردترین الگوهای طراحی، الگوی طراحی سینگلتون یا Singleton است که در این مطلب با آن آشنا میشویم.
singleton چیست؟
سینگلتون یک الگوی طراحی سازنده (Creational) است که به شما اطمینان میدهد از یک کلاس، تنها یک نمونه یا شی (Object) ساخته شود. بنابراین در هر زمان که استفاده از این کلاس نیاز شود، سینگلتون یک دسترسی سراسری به تنها شیای که از این کلاس ساخته شده است ایجاد میکند و اجازه ساخت شی جدیدی را از آن کلاس نمیدهد.
الگوی طراحی سینگلتون چه مشکلاتی را برطرف میکند
الگوی Singleton در یک زمان دو مشکل را حل کرده و تنها اصل مسئولیت (Single Responsibility) را نقض میکند:
این اطمینان را حاصل میکند که از یک کلاس تنها یک شی یا نمونه ساخته شود.
یک دسترسی سراسری به تنها شی ساخته شده از این کلاس، برقرار میکند.
اما شاید برای شما جای سوال باشد که چرا باید تعداد نمونههای یک کلاس بیشتر از یکی نباشد؟ رایجترین دلیل این امر کنترل دسترسی به برخی از منابع مشترک (Shared Resources) است. زیرا هر یک از این اشیای ساخته شده بخشی از منابع را به خود اختصاص میدهند. تصور کنید که شما یک شی از یک کلاس را ایجاد کردهاید، اما بعد از مدتی تصمیم به ایجاد یک شی دیگر از آن کلاس میگیرید.
بنابراین اگر از الگوی طراحی سینگلتون استفاده نکنید به جای استفاده از شی ای که قبلا ساخته اید، شی ای جدید میسازید که این کار از نظر مصرف منابع، هزینه بر و غیر بهینه است. جالب است بدانید که ممکن است حتی کلاینتها متوجه نشوند که در تمام مدت در حال استفاده از یک شی بودهاند.
اگر از الگوی طراحی سینگلتون استفاده نکنیم چه میشود؟
حکایت ضرب المثل معروف «آشپز که دوتا شد، غذا یا شور میشود یا بینمک» مصداق زمانی است که از این الگوی طراحی در موارد لازم استفاده نشود. تصور کنید که ما وظیفه آشپزی یک غذا را با استفاده از برنامه نویسی داریم. بنابراین کلاسی به نام آشپز ایجاد میکنیم تا بتوانیم روشهای پخت و پز (متدهای کلاس) را در آن تعریف کنیم. در نهایت کافی است از کلاس آشپز یک نمونه ایجاد کنیم که نام آن را آشپز اول میگذاریم.
حالا اگر در اینجا از الگوی طراحی سینگلتون استفاده نکنیم، این امکان وجود دارد که دوباره از کلاس آشپز نمونههای جدیدی ایجاد کنیم. در این صورت تعداد آشپزهای ما زیاد میشوند که با این کار علاوه بر اینکه فضا و مواد غذایی بیشتری (منابع سیستم) را مصرف میکنیم، به احتمال زیاد با غذایی بدمزه (نرم افزاری با مشکلاتی زیاد) مواجه خواهیم شد.
الگوی طراحی سینگلتون در برنامه نویسی
معمولا در در نرمافزارها وظیفه برقراری ارتباط با دیتابیسرا بر عهده یک شی قرار میدهند. بنابراین برای اینکه مطمئن شویم نمونهها و شیهای دیگری برای انجام این کار از کلاس ایجاد نمیشوند، بهترین کار استفاده از الگوی طراحی Singleton است. با این روش برای هر بار برقراری ارتباط با دیتابیس نیاز به ساخت شی جدیدی نخواهیم داشت و تنها با ساخت یک شی از کلاس DBConnection، برای همیشه از آن استفاده خواهیم کرد.
بر این اساس تنها یکبار از کلمه کلیدی new در کدهای خود استفاده خواهیم کرد و پس از آن فقط از تابع getInstance الگوی طراحی سینگلتون استفاده میکنیم. این تابع وظیفه دارد که دسترسی به تنها شی ای که از کلاس DBConnection ساخته شده است را برای ما فراهم کند. زیرا امکان وجود ساخت بیش از یک شی از روی این کلاس نه تنها ضرورتی ندارد و علاوه بر اینکه حافظه بیشتری مصرف میکند ممکن است منجر به بروز مشکل در نرم افزار نیز شود.
نحوه پیاده سازی الگوی طراحی سینگلتون
در این بخش از مقاله ابتدا آموزش روش پیاده سازی الگوی طراحی سینگلتون را مورد بررسی قرار خواهیم داد و سپس با استفاده از زبان برنامه نویسی PHP این الگو را برای کلاس DBConnection پیاده سازی میکنیم. نحوه پیادهسازی این الگو به صورت کلی به این گونه است که کلاس باید یک تابع داشته باشد تا یک شی از آن کلاس را در صورتی که قبلا ساخته نشده است، بسازد.
برای اطمینان از اینکه نمونه دیگری از این کلاس قابل ایجاد نباشد باید دسترسی به Constructor کلاس را به صورت Private تعریف کنیم. مراحل پیاده سازی سینگلتون عبارتند از :
اضافه کردن یک متغیر Static از نوع Private به کلاس مورد نظر به منظور ذخیرهسازی نمونه Singleton در آن.
نوشتن یک تابع Static از نوع Public برای دریافت نمونه ساخته شده Singleton (این تابع ورودی نخواهد داشت).
پیادهسازی "مقداردهی اولیه" متغیر در داخل تابع Static نوشته شده است. در این مرحله باید یک شی جدید در اولین فراخوانی ایجاد شود و مقدار آن را در متغیر Static قرار دهد. این تابع در فراخوانیهای بعدی باید شی ایجاد شده را فراخوانی کند.
باید Constructor مربوط به کلاس و کلاسی که از آن ارث بری شده است را Private کنیم. پس از این کار دیگر قادر نخواهیم بود در خارج از کلاس، از آن شی نمونه ای بسازیم و این کار فقط در همان کلاس امکانپذیر است.
در نهایت باید در کدهای خود به جای فراخوانی مستقیم Constructor سینگلتون (Direct calls) از تابع Static که نوشتهایم به این منظور استفاده کنیم.
<?php
/*
* DBConnection class - only one connection alowed
*/
class DBConnection {
private $connection;
private static $instance; //The static variable for store Singleton's instance
private $host = "HOSTt";
private $username = "USERNAME";
private $password = "PASSWORd";
private $database = "DATABASE";
/*
Get an instance of the DBConnection
@return Instance
*/
public static function getInstance():DBConnection {
if(self::$instance==null) { // If no instance then make one
$instance=self::$instance = new DBConnection ();
}
return $instance;
}
// Constructor
private function __construct() {
$this->connection = new mysqli($this->host, $this->username,
$this->password, $this->database);
}
}
?>
آشنایی با سایر الگوهای طراحی در برنامه نویسی شی گرا
سینگلتون، راهکاری برای تمامی مشکلات نیست و مانند هر ابزار دیگری تنها برای بخش کوچکی از مشکلات میتواند کارآمد باشد.
برنامه نویسان بسیاری به دلایل مختلف با استفاده از سینگلتون موافق نیستند. در ادامه به این مسئله بیشتر میپردازیم.
سینگلتون به دلیل این که وابستگیهای پنهانی ایجاد میکند، مانع از انجام تست واحد (unit) میشود. اگر آبجکتها و متدهای مرتبط با سینگلتون ارتباطی قوی با آن داشته باشند، بدون استفاده از کلاسهای تابعی، نوشتن کدهای قابل تست امکان پذیر نیست.
سینگلتون به دلیل اتصالی که در سراسر برنامه ایجاد میکند، اصلاح کد و ردیابی خطاها را دشوار میکند.
سینگلتون به شما امکان استفاده از کلاسهای انتزاعی و زیر کلاسها را نمیدهد.
چرا برنامه نویسان توصیه میکنند از سینگلتون استفاده نکنید؟
سینگلتون به عنوان یک ضد الگو یا Anti Pattern شناخته شده است. یک آنتی پترن، راهکاری با قابلیت استفادهی مجدد میباشد که بر خلاف این که در ابتدا مفید به نظر میرسد، در نهایت مشخص میشود استفاده کردن از آن مضراتی بیشتر از فواید آن دارد. دلایل زیادی میتواند منجر به آنتی پترن بودن یک راهکار شود؛ برای مثال استفاده از یک راهکار در زمان و شرایط خاص گذشته ممکن است مفید بوده باشد اما در شرایط فعلی این گونه نباشد یا این که الگوی مورد نظر از ابتدا راهکاری اشتباه باشد.
جمعبندی
شما تا اینجا فهمیدید Singleton چیست. با پیاده سازی الگوی طراحی سینگلتون مشکلات مربوط به ساخت غیر ضروری آبجکتهای مربوط به یک کلاس برطرف میشود. بنابراین اگر بارها و بارها از یک شی بسازیم و به استفاده از آن نیاز داشته باشیم، فقط یک نسخه از آن شی وجود دارد که در نرم افزار فراخوانی خواهد شد. با این کار تنها همان شی در حافظه ذخیره میشود و این امر باعث بهینهسازی در مصرف منابع خواهد شد. اما امروزه، سینگلتون رسما به عنوان یک آنتی پترن شناخته شده و استفاده از آن توصیه نمیشود.
آیا شما تا کنون از الگوی طراحی Singleton در نرمافزارهای خود استفاده کرده اید؟ نظر شما در مورد اینگونه الگوهای طراحی چیست؟
اگر به برنامه نویسی علاقه دارید و در یادگیری و بهبود مهارتهای خود نیاز به کمک دارید، شرکت در دورههای متخصص سون لرن را به شما پیشنهاد میکنیم. هدف مجموعه سون لرن افزایش سطح کیفیت آموزش و ساختن راهی برای ورود دانشجویان به بازار کار تخصصی است.
۱۶ دیدگاه
نازنین۰۷ دی ۱۳۹۹، ۱۲:۲۴
سلام. وقت بخیر. ارتباط الگوهای طراحی باهم و مزایا و معایب هرکدام را در فایلی دارید برای بنده ارسال بفرمایید.
نازنین کریمی مقدم۰۸ دی ۱۳۹۹، ۲۲:۵۷
سلام. به زودی مقاله ای رو بهشون اختصاص میدیم.
برای اطلاعات بیشتر توصیه میکنم کتاب Head First Design Patterns رو مطالعه کنید به شیوه جالبی الگوهای طراحی رو توضیح داده و در دورههای معتبر هم تدریس میشه.
omid۲۴ شهریور ۱۳۹۹، ۰۷:۰۷
بسیار عالی بود
نازنین کریمی مقدم۲۴ شهریور ۱۳۹۹، ۱۱:۰۹
ممنون که با ما همراه هستید.
علي۱۰ مرداد ۱۳۹۹، ۱۱:۴۷
سلام خسته نباشید.
گفتین دلیل استفاده سینگلتون دسترسی به برخی منابع مشترک( Shared Resources) هست خب مگه کاربرد استاتیک همین نیست؟
مثلا توی همین مثال دیتابیس اگه چند تا توابع نمایش اطلاعات دیتابیس داشته باشیم و استاتیک تعریف شوند فرقشون با اینکه استاتیک نباشن و از سینگلتون استفاده بشه چیه؟
کیوان علی محمدی۱۲ مرداد ۱۳۹۹، ۰۶:۰۴
سلام خدمت شما. کلا امروزه Singleton دیگه استفاده نمیشه مگر در موارد خیلی خیلی خاص. کلاسهای Singleton رو نمیشه به راحتی Test کرد و در سیستمهای Concurrency هم نمیتونه استفاده بشه. مباحث مثل ارتباط با دیتابیس هم با الگوهای طراحی مثل fly weight که نوعی از فکتوری محسوب میشه حل میشه.
علی۰۳ فروردین ۱۳۹۹، ۱۸:۲۹
سلام
عکسها لود نمیشن؟لطفا اصلاح کنین
سعیده۱۷ اسفند ۱۳۹۸، ۱۷:۱۶
ممنون توضیحات خوب و کاملی بود. فقط بعضی عکسها لود نمیشن. اگه با زبان C# هم میذاشتید عالی بود.
yousof۰۱ دی ۱۳۹۸، ۱۷:۴۲
مرسی از سایت خوبتون
F Y۰۲ آبان ۱۳۹۸، ۱۴:۱۷
مرسی از سایت خوبتون و اینکه رایگان مقاله میذارین
یک طرفدار۲۴ فروردین ۱۳۹۸، ۱۷:۳۷
من بک کامنت در الگو factory زدم که مربوط به این الگو است .لطفا جا به جا کنید
Don't use a Singleton pattern
Singleton is an anti-pattern. Paraphrased from Brian Button:
1- They are generally used as a global instance, why is that so bad? Because you hide the dependencies of your application in your code, instead of exposing them through the interfaces. Making something global to avoid passing it around is a code smell.
2- They violate the single responsibility principle: by virtue of the fact that they control their own creation and lifecycle.
3- They inherently cause code to be tightly coupled. This makes faking them out under test rather difficult in many cases.
4-They carry state around for the lifetime of the application. Another hit to testing since you can end up with a situation where tests need to be ordered which is a big no for unit tests. Why? Because each unit test should be independent from the other.
There is also very good thoughts by Misko Hevery about the root of problem.
به کدها هم توجه کنید.[codeBox]/--------------------------------------------------
//Bad:
class DBConnection
{
private static $instance;
private function __construct(string $dsn)
{
// ...
}
public static function getInstance(): DBConnection
{
if (self::$instance === null) {
self::$instance = new self();
}
return self::$instance;
}
// ...
}
$singleton = DBConnection::getInstance();
//---------------------------------------------------
//Good:
class DBConnection
{
public function __construct(string $dsn)
{
// ...
}
// ...
}
Create instance of DBConnection class and configure it with DSN.
$connection = new DBConnection($dsn);
//-----------------------------------------------------
//And now you must use instance of DBConnection in your application.[/codeBox]
مهدی علامه۲۵ فروردین ۱۳۹۸، ۰۷:۲۶
سلام دوست عزیز
تصحیح شد
مرسی از به اشتراک گذاشتن دیدگاهتون
مجید زادگی۲۱ فروردین ۱۳۹۸، ۱۴:۵۸
سلام
مقالههای خیلی خوبی ارائه میدید واقعاً ممنون.
من از این الگو طراحی بسیار استفاده میکنم در پروژهها و بهترین مثال هم همین کلاس دیتابیس بود که فرمودید.
لطفا در مورد الگوهای طراحی پر کاربرد دیگر هم مطالب بزارید.
متشکرم از شما و مجموع سون لرن.
مهدی علامه۲۲ فروردین ۱۳۹۸، ۰۵:۱۴
سلام مجید جان
خیلی خوشحالم که از این محتوا استقبال کردید و راضی بودید.
انشالله در برنامه محتوایی جدید، تمام الگوهای طراحی را به صورت جامع آموزش خواهیم داد.
تشکر
امیرمحمد رضائی۲۱ فروردین ۱۳۹۸، ۱۴:۳۷
سلام و درود خسته نباشید
تو سال جدید برنامه ای برای جاوا و پایتون ندارید ؟
لقمان آوند۲۲ فروردین ۱۳۹۸، ۰۶:۴۶
سلام
به امید خدا برای نیمه دوم سال 98 در برنامه مون هست.
راهنمای مقاله
singleton چیست؟
الگوی طراحی سینگلتون چه مشکلاتی را برطرف میکند
اگر از الگوی طراحی سینگلتون استفاده نکنیم چه میشود؟
الگوی طراحی سینگلتون در برنامه نویسی
نحوه پیاده سازی الگوی طراحی سینگلتون
معایب استفاده از الگوی طراحی سینگلتون
چرا برنامه نویسان توصیه میکنند از سینگلتون استفاده نکنید؟
راهنما و فهرست مقاله
singleton چیست؟
الگوی طراحی سینگلتون چه مشکلاتی را برطرف میکند
اگر از الگوی طراحی سینگلتون استفاده نکنیم چه میشود؟
الگوی طراحی سینگلتون در برنامه نویسی
نحوه پیاده سازی الگوی طراحی سینگلتون
معایب استفاده از الگوی طراحی سینگلتون
چرا برنامه نویسان توصیه میکنند از سینگلتون استفاده نکنید؟