Posted by: Ibrahim Ahmed | 2010/05/22

Decorator pattern

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

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

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

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



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

• الكائن الأصلي لا يوجد ولا يهم ان نضع به اي ديكور

• لا توجد class واحدة كبيرة بها جميع الخيارات .

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

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

المكونات :

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

العمليات :

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

IComponent:

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

Decorator:

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

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

العلاقة الثانية مرسومة على شكل معيين صغيير على الـ Decorator  و مرتبطة بـ Icomponent هذا يشير إلى أن الـ Decorator  من الممكن ان يكون مرتبط بمورث  واحد أو أكثر من الـ IComponent

ومن الممكن في اي لحظة لو اردنا جعل العمليات مختلفلفة ان نقوم بعمل 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:

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

سنقوم الان بعرض مثال عملي لكل ما ذكرناه سابقا.

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  أولا بدون أي ديكور عليها ثم قمنا بعد ذلك بعرض كيفية اضافة الديكور عليها وتاثيره على اصل الكلاس

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

مزخرف الصور  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 ونضع بها static 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 Pattern


الردود

  1. تم نقل الترجمة الى مدونتي التقنية
    http://ibrahimcsharp.blogspot.com


اترك رد

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / تغيير )

Twitter picture

You are commenting using your Twitter account. Log Out / تغيير )

Facebook photo

You are commenting using your Facebook account. Log Out / تغيير )

Connecting to %s

التصنيفات

Follow

Get every new post delivered to your Inbox.