النمط الديكوري (Decorator Pattern)
الدور او الوظيفة : decorator pattern يهدف الى توفير وسيلة لربط الحالات الجديدة والسلوك إلى كائن بطريقة ديناميكية. والكائن لا يعلم انه يجري عليه عملية "Decoration" ، الأمر الذي يجعل هذا النمط مفيد لتطور النظم. وهناك نقطة رئيسية في تنفيذ هذا النمط وهو انه يعمل على تزيين ال class الاصلي و حتى المورث منه على حد سواء.
توضيح : فإن decorator pattern تأخذ كائن موجود ويضيف إليه. وكمثال على ذلك ، النظر في الصورة التالية .هناك طرق عديدة للإضافة على الصورة ، مثل وضع الحدود من حولها أو تحديد العلامات ذات الصلة إلى المحتوى. يمكن عرض هذه الإضافات على رأس الصورة.
هناك أربعة أشياء نلاحظها
سوياً : الصورة الأصلية كما هو
موضح على اليمين ، الكائن الذي يوفر الحدود ، واثنين
من الكائنات المرتبطة بها. كل واحد منهم هو كائن ديكور. وبالنظر إلى عدد الطرق
سوياً : الصورة الأصلية كما هو
موضح على اليمين ، الكائن الذي يوفر الحدود ، واثنين
من الكائنات المرتبطة بها. كل واحد منهم هو كائن ديكور. وبالنظر إلى عدد الطرق
التي من الممكن ان بتم عمل decoration للصورة بها نجد انه لا حصر لها ، يمكن أن يكون لدينا الكثير من هذه الكائنات الجديدة. وميزة هذا النمط هو ما يلي :
• الكائن الأصلي لا يوجد ولا يهم ان نضع به اي ديكور
• لا توجد class واحدة كبيرة بها جميع الخيارات .
• اجزاء هذا ال pattern مستقلة عن بعضها البعض.
• ويمكن أن تتداخل الزخارف معا بطريقة ومزيج لتجميل الواجهه بطريقة افضل.
• ويمكن أن تتداخل الزخارف معا بطريقة ومزيج لتجميل الواجهه بطريقة افضل.
التصميم: يمكن تحديد العناصر الاساسية
في نمط الديكور في رسم تخطيطي UML كما هو موضح بالشكل التالي ولان هذا هو تصميمنا الاول سوف نشرحه تفصيليا . نقوم بعرض الاجزاء الاساسية في التصميم وهي:
في نمط الديكور في رسم تخطيطي 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.
موروثة من 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 (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 على العنصر ) ملزمة بان ترث من الانترفيس للصورة مثلا لكي تحوي ميثود او اثنين فنحن بذلك نحمل التاج كلاس كل مايوجد في كلاس الصورة لكي نستفيد بهاتين الدالتين مما يجعل الاوبجيكت كبير جدا بدون داعي.
ان التاج كلاس (وهي التي تقوم باضافة ال 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));
: ", 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
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
Classic FM to school
82 5. A-B-decorated : I am walking to school and
listening to Classic FM
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;
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
وغيرها من الاضافات على اغلب ال controls
وبذلك نكون قد انهينا ال Decorator pattern
الدرس القادم ان شاء الله Proxy Patter
ليست هناك تعليقات:
إرسال تعليق