1. ما هو نمط الديكور (Decorator Pattern)
نمط الديكور (Decorator Pattern) هو نمط تصميم هيكلي يتيح إضافة ميزات إضافية إلى كائن ديناميكياً دون تعديل في كود الكائن. يحقق ذلك عن طريق تغليف الكائن في فئة ديكور (Decorator)، مما يوفر القدرة على إضافة، وتعديل، أو تعزيز سلوك الكائن في وقت التشغيل.
2. السمات والمزايا لنمط الديكور (Decorator Pattern)
السمات والمزايا لنمط الديكور تشمل:
- تمديد الوظائف ديناميكياً لكائن دون تعديل في كوده.
- الامتثال لمبدأ الافتح-أغلق (Open-Closed Principle)، الذي يسمح بإضافة وإزالة ديكورات بطريقة ديناميكية.
- القدرة على تجميع عدة ديكورات لتحقيق تمديد وظائف متداخلة.
- استقلال الديكورات عن طريقة تزيين الكائن، مما يسمح بإجراء التغييرات بشكل مستقل.
3. أمثلة على التطبيقات العملية لنمط الديكور (Decorator Pattern)
يحتوي نمط الديكور على العديد من التطبيقات العملية في تطوير البرمجيات، مثل:
- إضافة وظيفة تسجيل (Logging) ديناميكياً
- إضافة وظيفة التخزين المؤقت (Caching) ديناميكياً
- التحقق الديناميكي من البيانات
4. تنفيذ نمط الديكور في لغة البرمجة جولانج (Golang)
4.1. الرسم البياني UML للفئات
4.2. تقديم المثال
في المثال، لدينا واجهة Component وفئة ConcreteComponent التي تنفذ طريقة Operation من واجهة Component.
ثم لدينا فئة Decorator، التي تنفذ أيضاً واجهة Component. تحتوي فئة Decorator على متغير عضو من نوع Component.
كلا من فئتي ConcreteDecoratorA وConcreteDecoratorB تورثان من الفئة Decorator وتنفذان وظائف إضافية عن طريق تجاوز طريقة Operation.
4.3. الخطوة 1: تعريف الواجهة وفئة التنفيذ
type Component interface {
Operation() string
}
type ConcreteComponent struct {}
func (c *ConcreteComponent) Operation() string {
return "عملية محددة للمكون"
}
4.4. الخطوة 2: تعريف الديكور (المزين)
type Decorator struct {
component Component
}
func (d *Decorator) Operation() string {
return d.component.Operation()
}
4.5. الخطوة 3: تنفيذ الديكور (المزين)
type ConcreteDecoratorA struct {
Decorator
addedState string
}
func (c *ConcreteDecoratorA) Operation() string {
c.addedState = "حالة جديدة"
return c.addedState + " " + c.component.Operation()
}
type ConcreteDecoratorB struct {
Decorator
}
func (c *ConcreteDecoratorB) Operation() string {
return "عملية محددة للديكور B " + c.component.Operation()
}
4.6. الخطوة 4: استخدام الديكور (المزين)
func main() {
component := &ConcreteComponent{}
decoratorA := &ConcreteDecoratorA{}
decoratorA.component = component
decoratorB := &ConcreteDecoratorB{}
decoratorB.component = decoratorA
result := decoratorB.Operation()
fmt.Println(result)
}
5. مقارنة نمط الديكور مع أنماط التصميم الأخرى
5.1. المقارنة مع التوريث (Inheritance)
بالمقارنة مع التوريث، يمكن نمط الديكور إضافة وظائف ديناميكياً دون تعديل في الكود الموجود، بينما التوريث ثابت ويحتاج إلى تحديد في وقت الترجمة.
5.2. المقارنة مع نمط البروكسي الثابت (Static Proxy Pattern)
يمكن أن يحقق كل من نمط الديكور ونمط البروكسي الثابت (Static Proxy Pattern) تمديد الوظائف، ولكن نمط الديكور أكثر مرونة ويسمح بإضافة وإزالة وظائف بشكل ديناميكي.
5.3. المقارنة مع نمط البروكسي الديناميكي (Dynamic Proxy Pattern)
كل من نمط الديكور ونمط البروكسي الديناميكي (Dynamic Proxy Pattern) يمكن أن يوسعوا وظائف الكائنات في وقت التشغيل. ومع ذلك، يقوم نمط الديكور بتزيين كائن واحد، بينما ينطوي نمط البروكسي الديناميكي على وصول غير مباشر بين كائن البروكسي وكائن الهدف.