النمط الديكوري (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



تم نقل الترجمة الى مدونتي التقنية
http://ibrahimcsharp.blogspot.com
By: Ibrahim Ahmed on 2010/05/24
at 10:50 ص