بحث هذه المدونة الإلكترونية

الأحد، 23 مايو 2010

Decorator pattern



النمط الديكوري (Decorator Pattern)



الدور او الوظيفة :  decorator pattern يهدف الى توفير وسيلة لربط الحالات الجديدة والسلوك إلى كائن بطريقة ديناميكية.  والكائن لا يعلم انه يجري عليه عملية "Decoration" ، الأمر الذي يجعل هذا النمط مفيد لتطور النظم. وهناك نقطة رئيسية في تنفيذ هذا النمط وهو انه يعمل على تزيين ال class  الاصلي و حتى المورث منه على حد سواء.



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

موضح على اليمين ، الكائن الذي يوفر الحدود ، واثنين
من الكائنات المرتبطة بها
. كل واحد منهم هو كائن ديكور. وبالنظر إلى عدد الطرق



التي من الممكن ان بتم عمل decoration للصورة  بها نجد انه لا حصر لها ، يمكن أن يكون لدينا الكثير من هذه الكائنات الجديدة.  وميزة هذا النمط هو ما يلي :

الكائن الأصلي لا يوجد ولا يهم ان نضع به اي ديكور
لا توجد class واحدة كبيرة بها جميع الخيارات .
اجزاء هذا ال pattern  مستقلة عن بعضها البعض.
 •
ويمكن أن تتداخل  الزخارف معا بطريقة ومزيج لتجميل الواجهه بطريقة افضل.


التصميم: يمكن تحديد العناصر الاساسية
في نمط الديكور في رسم تخطيطي
UML  كما هو موضح بالشكل التالي ولان هذا هو تصميمنا الاول سوف نشرحه تفصيليا . نقوم بعرض الاجزاء الاساسية في التصميم وهي:

المكونات :  

Class  اصلية من كائن معين سوف يضاف عليه او يتم تعديله ومن الممكن ان يكون اكثر من class

العمليات  :

العمليات في الانترفيس IComponent والتي من الممكن ان تستبدل.


IComponent:

هو ال interface الذي سيقوم بتحديد هوية ال classes  التي سيتم تزيينها.

Decorator:

وهي ال class  المشتقة من ال interface  السابق والذي يقوم باضافة السلوك والديكور. ومن الممكن ان يكون اكثر من class


 نرى في منتصف هذا التصميم طبقة ال decorator  ومرتبطة بعلاقتين مع ال IComponent interface
العلاقة الاولى :

تبدأ بسهم منقط من decorator لIComponent وهي تشير الى ان ال Decorator
موروثة  من 
IComponent  وهذا يعني ان ال decorator object  من الممكن ان تستخدم في المكان الذي تتوقع فيه استخدم الانترفيس وبالتالي يمكن لل client  استخدام Component  و Decorator ديكور بالتبادل ،هذا هو اساس ال Decorator pattern.


العلاقة الثانية مرسومة على شكل معيين صغيير على الـ Decorator  و مرتبطة بـ  Icomponent هذا يشير إلى أن الـ Decorator  من الممكن ان يكون مرتبط بمورث  واحد أو أكثر من الـ  Component ومن الممكن في اي لحظة لواردنا جعل العمليات مختلفة ان نقوم بعمل override   وبهذه لطريقة يكون قد تحقق الهدف من ال pattern

مثال : عند الرغبة في وضع  اضافتين على صورة مثلا نقوم بعمل الاتي


Photo photo = new Photo( );     
Tag  foodTag = new
Tag (photo, "Food",1);
Tag colorTag = new Tag (foodTag, "Yellow",2);


في البداية قمنا بعمل صورة من ال class  photo بعد ذلك قمنا بالإضافة عليه بوضع foodTag  ثم قمنا بوضع اضافة اخرى على الاوبجيكت foodTag عن طريق ال colorTag

في معظم النماذج التي سنواجهها ، يمكن للعناصر الظهورفي اشكال مختلفة. وللحفاظ  على مخططات UML واضحة وبسيطة ليس علينا اظهار كل الخيارات فيها ولكن علينا ان ندقق ما اذا كانت الخيارات
مؤثرة في التصميم ام لا.

ينبغي أن ننظر في الآثار المترتبة على النمط من تعدد وجود هذه العناصر في التصميم :


تعدد المكونات Multiple components :

ان مختلف المكونات التي تطابق الانترفيس من الممكن ايضا ان "يضاف اليها"  فمثلا من الممكن ان يكون لدينا كلاسات ترسم اشخاص ومنازل وسفن من خلاص اشكال هندسية وخطوط فهذه الكلاسات من الممكن ايضا ان يضاف عليها حالات من خلال ال decorator . وهذا هو عمل ال IComponent

   الديكورات المتعددة    Multiple decorators :

كما اننا من الممكن ان نورث الانترفيس لاكثر من كلاس كذلك يمكننا ان يكون لدينا اكثر من انترفيس ولكل واحد منهم وظيفة . فاحدهما يضيف بوردر والاخر يضيف نص على الصورة وهكذا  

العمليات المتعددة Multiple operations:

فالعمليات التي تجري داخل الكلاس او الاوبجيكت بعض منها جزء من الاوبجيكت الاصلي والبعض من الانترفيس. 

 المحتوى Implementation:

نموذج او نمط decorator هو الذي لا يرتبط باي انترفيس ليستمد حالات اخرى behaviors. فلو
ان التاج كلاس (وهي التي تقوم باضافة ال decor على العنصر ) ملزمة بان ترث من الانترفيس للصورة مثلا لكي تحوي ميثود او اثنين فنحن بذلك نحمل التاج كلاس كل مايوجد في كلاس الصورة لكي نستفيد بهاتين الدالتين مما يجعل الاوبجيكت كبير جدا بدون داعي. 
سنقوم الان بعرض مثال عملي
لكل ما ذكرناه سابقا.

using System; 

class DecoratorPattern

{

    // Decorator Pattern Judith Bishop Dec 2006

    // Shows two decorators and the output of various

    // combinations of the decorators on the basic component


    interface IComponent

    {

        string Operation();

    }


    ////////////////////////////////////////////////////////////
  

    class Component: IComponent

    {
        public string Operation()

        {
            return "I am walking ";

        }

    }

///////////////////////////////////////////

   
    class DecoratorA: IComponent

    {
        IComponent component;

        public DecoratorA(IComponent c)

        {

            component = c;

        }

        public string Operation()

        {
            string s =component.Operation();

            s += "and listening to Classic FM ";

            return s;
        }

    }


///////////////////////////////////////////

   
    class DecoratorB : IComponent
    {
        IComponent component;

        public string addedState = "past the Coffee Shop ";

        public DecoratorB(IComponent c)

        {
            component = c;
        }



        public string Operation()

        {

            string s =component.Operation();
            s += "to school ";
            return s;

        } 

        public string AddedBehavior()

        {
            return "and I bought a cappuccino ";
        }

    }


    /// ///////////////////////////////////////////

   
    class Client

    {

        static void Display(string s, IComponent c)
        {

            Console.WriteLine(s + c.Operation());

        }

        static void Main()

        {
            Console.WriteLine("Decorator Pattern\n");


            IComponent component = new Component();

            Display("1. Basic component: ", component);

            Display("2. A-decorated : ", new DecoratorA(component));

            Display("3. B-decorated
: "
, new DecoratorB(component));

            Display("4. B-A-decorated : ", new DecoratorB(
            new DecoratorA(component)));

            // Explicit DecoratorB

            DecoratorB b = new DecoratorB(new Component());

            Display("5. A-B-decorated : ", new DecoratorA(b));

            // Invoking its added state and added behavior

            Console.WriteLine("\t\t\t" + b.addedState + b.AddedBehavior());
        }
    }
}

/* Output

76 Decorator Pattern

77

78 1. Basic component: I am walking

79 2. A-decorated : I am walking and listening to Classic
FM

80 3. B-decorated : I am walking to school

81 4. B-A-decorated : I am walking and listening to
Classic FM to school

82 5. A-B-decorated : I am walking to school and
listening to Classic FM

83 past the Coffee Shop and I bought a cappuccino

84 */

يبدا المثال كما رئينا بـ IComponent interface وكلاس بسيط موروثة من هذا الانترفيس وايضا يوجد اثنين من ال Decorator calssوالموروثة ايصا من نفس الـ IComponent 

الكلاس DecoratorA هي واضحة وبيسطة حيث انها لم تقوم سوى باضافة نص على العنصر الاصلي . اما الكلاس DecoratorB فقد قامت بنفس الوظائف الموجودة في الانترفيس ولكن اضافت عليه انها اضافت متغير جديد لم يكن موجود في الانترفيس وكذلك اضافت دالة جديدة 

وكما رئينا في دالة الـ Mainفقد قمنا بالعمل على الكلاس component  أولا بدون أاستخدام DecoratorA او DecoratorB عليها ثم قمنا بعد ذلك بعرض كيفية اضافة الديكور عليها وتاثيره على اصل الكلاس



الآن نستعرض مثالا اكثر واقعية :

مزخرف الصور  Photo Decorator

في هذا المثال سنستعرض كيفية تنفيذ النموذج decorator  على الصور، كما سنوضح:


using
System.Windows.Forms;

namespace DesignPatterns_Decorator
{
   // The original Photo class

    partial class Photo : Form
    {
        Image image;

        public Photo()
        {
            image = new Bitmap("jug.bmp");
            this.Text = "Lemonade";
            this.Paint += new PaintEventHandler(Drawer);
        }
        public virtual void Drawer(Object source, PaintEventArgs e)
        {
          e.Graphics.DrawImage(image, 5, 5,image.Width,image.Height);
        }

        static void Main()

        {
            Application.Run(new Photo());
        }
    }
} 

في البداية وضعنا form  أسميناها Photo  ثم اضفنا بداخلها صورة ترسم في اثناء حدث ال paint  للفورمة الآن و دون اي تغيير في class الصورة ، يمكننا البدء في إضافة الديكور.

سوف نقوم اولا برسم حدود زرقاء حول الصورة

public partial class BorderedPhoto : Photo
    {
       public  Photo photo;
        Color color;
        public BorderedPhoto(Photo p, Color c)
        {
            photo = p;
            color = c;
        } 

        public override void  Drawer(object source, PaintEventArgs e)
        {
            photo.Drawer(source, e);
            e.Graphics.DrawRectangle(new Pen(Color.Red, 1), 0, 0, image.Width + 10, image.Height + 10); 
        }
}

كما نرى في المثال قمنا بتوريث الكلاس Photo  للكلاس BorderedPhoto ثم قمنا بعمل Override للحدث Drawer لكي يقوم برسم مستطيل حول الصورة  ولكن لاحظ أن هذا الكود يختلف عن المثال السابق  في أن ليس هناك الانترفيس  IComponentوهذا هو طبيعي ومقبول تماما  فاننا يمكن أن نورث الديكور مباشرة  . الآن بعد أن ادرجنا Method  Derawer  في الكلاسين نستطيع ان نستدعيها من اي كلاس .
والان ، دعونا نعود نقوم ببعض الاضافات . سوف نقوم باضافة كلاس TaggedPhoto ونضع بها
array لتخزين اسماء الديكورات . ونستطيع استدعاء اي ديكور موجود في ال array عن طريق الدالة ListTaggedPhotos(


class TaggedPhoto : Photo
    { 
        Photo photo;
        string tag;
        int number;
        static int count;

        List<string> tags = new List<string>();

        public TaggedPhoto(Photo p, string t)
        {
            photo = p;
            tag = t;
            tags.Add(t);
            number = ++count;
        }
        public override void Drawer(Object source, PaintEventArgs e)
        {
            photo.Drawer(source, e);
            e.Graphics.DrawString(tag,
            new Font("Arial", 16),new SolidBrush(Color.Black), new PointF(80, 100 + number * 20));
        }
        public string ListTaggedPhotos()
        {
            string s = "Tags are: ";
            foreach (string t in tags) 
                  s+= t + " ";
            return s;

        }

        static void Main()

        {

            // Application.Run acts as a simple client
            Photo photo;
            TaggedPhoto foodTaggedPhoto, colorTaggedPhoto, tag;
            BorderedPhoto composition;

            // Compose a photo with two TaggedPhotos and a blue BorderedPhoto
            photo = new Photo();
            Application.Run(photo);
            foodTaggedPhoto = new TaggedPhoto(photo, "Food");
            colorTaggedPhoto = new TaggedPhoto(foodTaggedPhoto, "Yellow");
            composition = new BorderedPhoto(colorTaggedPhoto, Color.Blue);
            Application.Run(composition);
            Console.WriteLine(colorTaggedPhoto.ListTaggedPhotos());

            // Compose a photo with one TaggedPhoto and a yellow BorderedPhoto

            photo = new Photo();
            tag = new TaggedPhoto(photo, "Jug");
            composition = new BorderedPhoto(tag, Color.Yellow);
            Application.Run(composition);
            Console.WriteLine(tag.ListTaggedPhotos());
        }    
    }

الاستخدام:

هذه اربعة طرق لاستخدام الـ Decorator pattern في البرامج

-        في الاستخدام في الجرافيكس وفي العمل على الفيديو والصوت . حيث يمكننا ان ندخل الصوت
على الترجمة الفورية مثلا ومن ثم يكون المخرج هو الصوت مترجم او ضغط ملفات الصوت
والفيديو.
-        فيالاستخادم في واجهات البرامج

-        الاستخدام في واجهات الهواتف النقالة ومتصفحات الويب للهواتف النقالة
-        تجدها ايضا في كثير من الكلاسات الموجودة في ال .Net مثل ال Listbox فتجد اضافة البوردر
وغيرها من الاضافات على اغلب ال
controls


وبذلك نكون قد انهينا ال Decorator pattern
 الدرس القادم ان شاء الله Proxy Patter




ليست هناك تعليقات:

إرسال تعليق