در توسینسو تدریس کنید

و

با دانش خود درآمد کسب کنید

آموزش دیزاین پترن (Design Patterns) قسمت 3 : Singleton و Adapter

با سلام به همه همراهان Itpro.ir در بخش های قبل در مباحثی کلی درباره ی شی گرایی و الگوهای طراحی مطرح شد و از این بخش به بعد به تک تک الگو ها می پردازیم و هرکدام را توضیح خواهیم داد. در این بخش به الگوهای Singleton و Adapter می پردازیم که نسبتا الگوهای ساده تری هستند می پردازیم.

الگوی Singleton

این الگو یکی از الگوهای سازنده (creational) است و در ساختن اشیا از کلاس ها کاربرد دارد. این الگو یکی از معروف ترین الگوهای مطرح شده توسط Gang of Four است.

نکته: کلمه کاربر که در این سری مقالات استفاده می شود به کلاس هایی اطلاق می شود که از کلاس مورد بحث استفاده می کنند و منظور تنها کاربر انسانی نیست و ممکن است که یک زیرسیستم یا یک کلاس و یا حتی تابع main برنامه باشد.

حالتی را در نظر بگیرید که کلاسی دارید که می خواهید در کل برنامه خود فقط یک شی از این کلاس ساخته شود و اجازه ساخته شدن نمونه جدید به استفاده کنندگان از این کلاس داده نشود. این عمل در مواقعی بسیار لازم و ضروری است. به طور مثال حالتی را درنظر بگیرید که در برنامه کلاسی وجود دارد که واسط تنظیمات برنامه است یعنی برنامه هنگام لود شدن تنظیمات خود را در آن بارگزاری می کند. حال اگر این شی یک شی در کل برنامه یکتا نباشد و هر بخش از برنامه که بخواهد تنظیمات را بخواند و یا تغییری در آن ایجاد کند مجبور باشد که یک شی جدید از کلاس تنظیمات بسازد مدیریت این اشیا مشکل می شود و همچنین باعث نابه سامانی در تنظیمات خواهد شد. راه حل این مسئله این است که از همان اول کلاس به گونه ای تعریف شود که اجازه ساختن نمونه جدید به کاربران ندهد و تنها کاربران بتوانند یک نمونه از کلاس به وجود آورند.

اگر به برخی از قسمت های سیستم عامل ویندوز هم نگاهی بکنیم متوجه می شویم که چنین مکانیزمی در آن تعبیه شده است مثلاً یک سیستم فایل برای مدیریت فایل ها وجود دارد و یا با وجود این که ممکن است چندین پرینتر به کامپیوتر متصل باشد ولی فقط یک printer spooler در سیستم وجود دارد.

برای به وجود آوردن چنین مکانیزمی شاید اولین راه حل این باشد که یک شی سراسری در کل برنامه تعریف شده و هروقت که برنامه ای بخواهد از این کلاس استفاده کند از شی سراسری تعریف شده استفاده کند. اما مشکلی که این راه حل دارد این است که این راه حل مانع ساخت شی جدید از کلاس نمی شود. یک راه حل بهتر این است که خود کلاس را مسئول این کار قرار دهیم که مواظب باشد که یک نمونه بیشتر نداشته باشد. این کار را الگوی Singleton انجام می دهد.

روش کار این الگو به این صورت است که یک نمونه از خود کلاس در داخل کلاس ساخته می شود و هر وقت که کلاس های دیگر خواستند از این کلاس استفاده کنند تنها می توانند به این نمونه ساخته شده دسترسی داشته باشند و نمی توانند از کلاس نمونه سازی کنند.

ساختار این الگو به این شکل است:

ساختار الگوی Singleton

همانطور که در شکل می بینید یک فیلد به نام instance داریم که از خود کلاس است و این فیلد تنها الگویی که قرار است ساخته شود را در خود نگهداری می کند. سازنده(constructor) این کلاس از نوع private بوده بنابراین نمی توان از این کلاس یک نمونه جدید توسط عملگر new ایجاد کرد. تنها راه ساختن یک نمونه از این کلاس فراخوانی متد GetInstance است.

ویژگی های این الگو

این الگو باعث می شود که دسترسی به نمونه های کلاس کنترل شود و تعداد خاص از نمونه ها ایجاد شود.

با استفاده از این الگو نیاز به ساختن متغیر های سراسری در فضای namespace کمتر شده و از اشغال فضای namespace توسط متغیرهای سراسری جلوگیری می کند.

کد پیاده سازی این الگو در زبان C# به شکل زیر است

  public sealed class Singleton
    {
       private Singleton()
        {
        }
        private readonly static Singleton instance=new Singleton();
        public static Singleton UniqueInstance { get { return instance; } }
    }

همانطور که درکد مشاهده می شود یک بار یک نمونه از کلاس ساخته می شود و تنها راه دسترسی به کلاس استفاده از ویژگی UniqueInstance می باشد که نمونه ساخته شده را برمی گرداند. در این نوع پیاده سازی از همان ابتدا یک نمونه از کلاس ساخت شده است و استفاده می شود ولی می توان این نمونه را وقتی ساخت که درخواستی برای اولین باربه کلاس ارسال شود که به این شیوه Lazy instantiate گفته می شود. کد این روش به شکل زیر است.

 public class LazySingleton
    {
        private LazySingleton(){}
        private static LazySingleton instance;

        public static LazySingleton UniqueInstance
        {
            get { return instance!=null?instance:new LazySingleton();}
        }

    }

کدی که آورده شده است تا حدی می تواند هدف این الگو را که ساخته شدن یک نمونه از کلاس باشد را برآورده کند ولی اگر برنامه دارای چند thread باشد ممکن است در مواردی بیشتر از یک نمونه از کلاس ساخته شود که در اصطلاح گفته می شود برنامه Thread Safe نیست که به جای کد بالا از کد پایین استفاده می کنیم.

    public class Singleton
    {
        private Singleton(){}

        class SingletonCreator
        {
            static SingletonCreator()
            {
            }
            internal static readonly Singleton UniqueInstance = new Singleton(); 
        }

        public static Singleton UniqueInstance
        {
            get { return SingletonCreator.UniqueInstance; }
        }
    }

در کد بالا از یک کلاس داخلی برای ساخت نمونه از کلاس استفاده می شود که علاوه بر Thread safe بودن خاصیت Lazy instatiate هم دارد.

حال اگر نیاز باشد که از یک خانواده کلاس ها تنها یک نمونه ایجاد شود پیاده سازی این الگوی کمی متفاوت تر خواهد شد. برای مثال تصور کنید که یک کلاس میوه دارید که از آن کلاس های دیگری مانند سیب و پرتقال مشتق شده اند و لازم است که در برنامه فقط یک عدد میوه وجود داشته باشد یعنی اگر یک سیب وجود دارد دیگر نتوان میوه ی دیگری(چه سیب و چه پرتقال) ایجاد کرد. به منظور پیاده سازی چنین روشی به شکل زیر عمل می کنیم.

 public  class SingleFruit
        {
            public string name;
            protected SingleFruit()
            { }
            protected static  SingleFruit Instance ;
            public static SingleFruit UniqueInstance
            {
                get { return Instance; }
            }
        }

        public class SingleApple : SingleFruit
        {
           
            private SingleApple()
            {
                name = "Apple";
            }
            public static SingleFruit UniqueInstance
            {
                get { return Instance != null ? Instance :Instance= new SingleApple(); }
            }
        }

        public class SingleOrange : SingleFruit
        {
            private SingleOrange()
            {
                name = "Orange";
            }
            public static SingleFruit UniqueInstance
            {
                get { return Instance != null ? Instance :Instance= new SingleOrange(); }
            }

        }

با توجه به کد بالا اگر به کلاس های فرزندان مقداردهی نشده باشد استفاده از کلاس پدر باعث خطای NullReference خواهد شد. و همچنین این کد Thread safe نمی باشد.

الگوهای مرتبط با الگوی Singleton الگوهای Façade و Abstract Factory و بسیاری از الگوهای دیگر هستند که لازم است در برنامه از آنها فقط یک نمونه ساخته شده باشند که در زمان معرفی این الگوها توضیحات لازم ارایه خواهد شد.

الگوی Adapter

همان گونه که از اسم این الگو مشخص است، هنگامی که دو کلاس واسط(Interface) های غیرمرتبط با یکدیگر داشته باشند این الگو واسط یکی را به دیگری تبدیل می کند که بتوانند با یکدیگر ارتباط برقرار کند.از این الگو که یک الگوی ساختاری است زمانی استفاده می شود که در یک برنامه بخواهیم که دو کلاس غیرمرتبط با یکدیگر کار کنند. این الگو در برنامه هایی که از کلاس های آماده استفاده می شود و یا از کلاس هایی استفاده می شود که قبلا نوشته شده اند و به گونه ای باشد که طراحان نرم افزار اجازه تغییر در این کلاس ها را نداشته باشند. بسیاری از مثال های الگوی Adapter درگیر ورودی و خروجی هستند زیرا این دامنه ای است که همیشه تغییر می کند.

برای مثال برنامه هایی که در دهه 80 میلادی نوشته شده اند دارای UI بسیار ضعیف تری نسبت به برنامه های نوشته شده در هزاره سوم میلادی هستند. حال اگر دوباره نویسی همان برنامه های قبلی به صرفه نباشد و بخواهیم این برنامه ها با سخت افزارهای جدید همخوانی و سازگاری داشته باشند باید برنامه ای طراحی کنیم که بین این دو برنامه یعنی برنامه قدیمی و راه اندازهای سخت افزارهای جدید قرار بگیرد و بین این دو برنامه ارتباط برقرار کند. به برنامه ای که بین این دو برنامه قرار می گیرد Adapter گویند. حال شاید به نظر برسد که استفاده از Adapter اصلاً نیاز نباشد و می توان واسط یکی از کلاس ها را تغییر داد تا بتواند کار کند، این کار زمانی ممکن است که بتوان به کد کلاس ها دسترسی داشت و تغییر کلاس ها باعث به وجود آمدن مشکل در برنامه نباشد و پیچیدگی برنامه را افزایش ندهد که در اکثر مواقع این عمل به صرفه نیست.

ساختار این الگو به این شکل است:

ساختار الگوی Adapter

همانطور که در شکل دیده می شود کلاس Adapter از اینترفیس ITarget مشتق شده است و در یکی از متدهایی که در این کلاس وجود دارد (Request) متد مورد نظر را از کلاس Adaptee فراخوانی می کند. کلاس Adapter متدهای Adaptee را با متدها و خصوصیای Target وفق می دهد. به طور کلی اینترفیس ITarget همان اینترفیسی است که کاربر آن را پیاده سازی می کند و کلاس Adaptee همان کلاسی است که باید با برنامه وفق داده شود. کاربران برای استفاده فقط از کلاس Adapter نمونه سازی می کنند و نیازی به درگیری با ارتباط Target و Adaptee نخواهند بود.

کد این الگو به شرح زیر است.

using System;

    public class Adaptee
    {
        public double SpecificRequest(double a, double b)
        {
            return a/b;
        }
    }

    public interface ITarget
    {
        string Request(int a);
    }

    public class Adapter : Adaptee, ITarget
    {
        public string Request(int a)
        {
            return "the result is" + (int) Math.Round(SpecificRequest(a, 5));
        }
    }

Adapter دوطرفه

ساده ترین شکل الگوی Adapter شکل یک طرفه آن است که یک کلاس یا اینترفیس Target بوده و کلاس دیگر نقش Adaptee را داشت که تا این قسمت شرح داده شد. حال فرض کنید که می خواهیم هر دو طرف هم نقش Adaptee و هم نقش Target را داشته باشند. به گونه ای که از کلاس Adapter بتوان همه به شکلی استفاده کرد که متدهای هر دو طرف را داشته باشند. در این صورت کلاس Adapter دارای متدها و خصوصیات هر دو کلاس می باشد. یک Adapter دو طرفه می تواند به جای هر دو کلاس Target و Adaptee مورد استفاده قرار بگیرد. برای این کار در زبانهایی مانند ++C که وراثت از چندین کلاس را پشتیبانی می کنند کار آسانی است. به این شکل که کلاس Adapter از دو کلاس دیگر ارث بری داشته باشد همانند ساختار زیر

آموزش دیزاین پترن (Design Patterns) قسمت 3 : Singleton و Adapter

اما در بسیاری از زبان های برنامه نویسی رایج مانند #C و Java ارث بری از چندکلاس پشتیبانی نمی شود. بنابراین این راه حل در این زبان ها پاسخگو نیست. برای این که در این زبان ها از Adapter دو طرفه استفاده کنیم به شکل زیر عمل می کنیم.

  public class Adaptee
    {
        public double SpecificRequest(double a, double b)
        {
            return a/b;
        }
    }

    public interface ITarget
    {
        string Request(int a);
    }

    public class Adapter : Adaptee, ITarget
    {
        public string Request(int a)
        {
            return "the result is" + (int) Math.Round(SpecificRequest(a, 5));
        }
    }

////////////////////////////////////////////////////////
internal class Program
    {
        private static void Main(string[] args)
        {
            ITarget aa = new Adapter();
            Console.WriteLine(aa.Request(5));
            Console.WriteLine((aa as Adaptee).SpecificRequest(8, 3));
         }

    }

در کد بالا یکی از اینترفیس ها (IAircraft ) که همان Target است را در کلاس Adapter پیاده سازی می کنیم و از کلاس Adaptee ارث بری می نماییم. ساختار این عمل شبیه به همان استفاده از الگوی Adapter به شکل یک طرفه آن است ولی در استفاده به این شکل عمل می کنیم. همانطور که مشاهده می شود می توان از کلاس Adapter هم به عنوان یک نمونه از Target و هم به عنوان یک نمونه از Adaptee استفاده نمود. مشکلی که در زبان هایی مانند C# یا Java وجود دارد این است که باید Target را به عنوان اینترفیس معرفی کرده و در کلاس Adapter آن را پیاده سازی نمود ولی در زبانی مانند C++ چنین محدودیتی وجود ندارد.

نویسنده : مهدی عادلی

منبع : جزیره برنامه نویسی وب سایت توسینسو

هرگونه نشر و کپی برداری بدون ذکر منبع و نام نویسنده دارای اشکال اخلاقی می باشد.

#آموزش_design_pattern #الگوی_singleton #الگوهای_طراحی #الگوهای_طراحی_در_نرم_افزار #طراحی_و_توسعه_نرم_افزار #الگوی_adapter #آموزش_برنامه_نویسی #الگوهای_طراحی_شی_گرا
عنوان
1 آموزش دیزاین پترن (Design Patterns) قسمت 1 : اطلاعات پایه رایگان
2 آموزش دیزاین پترن (Design Patterns) قسمت 2 : معرفی الگوها رایگان
3 آموزش دیزاین پترن (Design Patterns) قسمت 3 : Singleton و Adapter رایگان
4 آموزش دیزاین پترن (Design Patterns) قسمت 4 : الگوی Observer رایگان
5 آموزش دیزاین پترن (Design Patterns) قسمت 5 : الگوی Iterator رایگان
6 آموزش دیزاین پترن (Design Patterns) قسمت 6 : الگوی Command رایگان
7 آموزش دیزاین پترن (Design Patterns) قسمت 7 : الگوی Mediator رایگان
8 آموزش دیزاین پترن (Design Patterns) قسمت 8 : الگوی Memento رایگان
9 آموزش دیزاین پترن (Design Patterns) قسمت 9 : الگوی Strategy رایگان
10 آموزش دیزاین پترن (Design Patterns) قسمت 10 :الگوی Factory Method رایگان
11 آموزش دیزاین پترن (Design Patterns) قسمت 11 : الگوی State رایگان
زمان و قیمت کل 0″ 0
2 نظر

این نظر توسط مهدی عادلی در تاریخ يكشنبه, 22 شهریور 1394 حذف شده است.

دلیل: مطلب فاقد بار فنی بوده و جنبه تبلیغاتی دارد

حسن بابایی

ممنون استاد بسیار ساده و قابل فهم توضیح دادید .

نظر شما
برای ارسال نظر باید وارد شوید.
از سرتاسر توسینسو
تنظیمات حریم خصوصی
تائید صرفنظر
×

تو می تونی بهترین نتیجه رو تضمینی با بهترین های ایران بدست بیاری ، پس مقایسه کن و بعد خرید کن : فقط توی جشنواره تابستانه می تونی امروز ارزونتر از فردا خرید کنی ....