نمط التصميم الديكوري يستخدم لتعديل وظيفة كائن ما أثناء التشغيل. في الوقت نفسه، لن تتأثر الحالات الأخرى من نفس الصنف بهذا، لذلك يحصل الكائن الفردي على السلوك المعدل. نمط التصميم الديكوري هو أحد أنماط التصميم الهيكلي (مثل نمط التكيف، نمط الجسر، نمط المكون) ويستخدم الفصول الفنية الأو المعرفات النموذجية مع التركيب للتنفيذ.
نمط التصميم الديكوري
نستخدم التوريث أو التكوين لتمديد سلوك كائن ما ولكن هذا يتم في وقت الترجمة وهو قابل للتطبيق على جميع الحالات من الصنف. لا يمكننا إضافة أي وظيفة جديدة أو إزالة أي سلوك موجود في وقت التشغيل – هذا حين يدخل نمط الديكور في الصورة. لنفترض أننا نريد تنفيذ أنواع مختلفة من السيارات – يمكننا إنشاء واجهة Car لتعريف طريقة assemble ثم يمكننا أن نمتلك سيارة أساسية، ويمكننا أن نمتد ذلك إلى سيارة رياضية وسيارة فاخرة. ستبدو الهرمة التنفيذية كما في الصورة أدناه. ولكن إذا أردنا الحصول على سيارة في وقت التشغيل تحتوي على ميزات كل من سيارة رياضية وسيارة فاخرة، فإن التنفيذ يصبح معقدًا وإذا أردنا تحديد أي ميزات يجب أن تضاف أولاً، فإن الأمر يصبح أكثر تعقيدًا. الآن تخيل إذا كان لدينا عشرة أنواع مختلفة من السيارات، فإن منطق التنفيذ باستخدام التوريث والتكوين سيكون من المستحيل إدارته. لحل هذا النوع من الوضع البرمجي، نطبق نمط الديكور في جافا. نحتاج إلى وجود الأنواع التالية لتنفيذ نمط تصميم الديكور.
-
واجهة المكون – الواجهة أو الفئة المجردة التي تعرف الطرق التي سيتم تنفيذها. في حالتنا ستكون
Car
واجهة المكون.package com.journaldev.design.decorator; public interface Car { public void assemble(); }
-
تنفيذ المكون – التنفيذ الأساسي لواجهة المكون. يمكننا أن نمتلك فئة
BasicCar
كتنفيذ للمكون.package com.journaldev.design.decorator; public class BasicCar implements Car { @Override public void assemble() { System.out.print("Basic Car."); } }
-
المزين – تقوم فئة المزين بتنفيذ واجهة المكون ولديها علاقة HAS-A مع واجهة المكون. يجب أن يكون من الممكن الوصول إلى متغير المكون من قبل فئات المزين الفرعية، لذا سنقوم بجعل هذا المتغير محميًا.
package com.journaldev.design.decorator; public class CarDecorator implements Car { protected Car car; public CarDecorator(Car c){ this.car=c; } @Override public void assemble() { this.car.assemble(); } }
-
مزينات محددة – توسيع وظائف المزين الأساسي وتعديل سلوك المكون وفقًا لذلك. يمكننا أن نمتلك فئات مزينة محددة كـ
LuxuryCar
وSportsCar
.package com.journaldev.design.decorator; public class SportsCar extends CarDecorator { public SportsCar(Car c) { super(c); } @Override public void assemble(){ super.assemble(); System.out.print(" إضافة ميزات سيارة رياضية."); } }
package com.journaldev.design.decorator; public class LuxuryCar extends CarDecorator { public LuxuryCar(Car c) { super(c); } @Override public void assemble(){ super.assemble(); System.out.print(" إضافة ميزات سيارة فاخرة."); } }
نمط تصميم المزين – مخطط الصفوف
برنامج اختبار نمط تصميم المزين
package com.journaldev.design.test;
import com.journaldev.design.decorator.BasicCar;
import com.journaldev.design.decorator.Car;
import com.journaldev.design.decorator.LuxuryCar;
import com.journaldev.design.decorator.SportsCar;
public class DecoratorPatternTest {
public static void main(String[] args) {
Car sportsCar = new SportsCar(new BasicCar());
sportsCar.assemble();
System.out.println("\n*****");
Car sportsLuxuryCar = new SportsCar(new LuxuryCar(new BasicCar()));
sportsLuxuryCar.assemble();
}
}
لاحظ أن برنامج العميل يمكنه إنشاء أنواع مختلفة من الكائنات في وقت التشغيل ويمكنهم أيضًا تحديد ترتيب التنفيذ. مخرجات البرنامج الاختباري أعلاه هي:
Basic Car. Adding features of Sports Car.
*****
Basic Car. Adding features of Luxury Car. Adding features of Sports Car.
نمط تصميم المزين – نقاط مهمة
- نمط تصميم المزين مفيد في توفير قدرات التعديل أثناء التشغيل وبالتالي أكثر مرونة. من السهل الحفاظ عليه وتوسيعه عندما يكون عدد الخيارات أكبر.
- عيب نمط تصميم المزين هو أنه يستخدم الكثير من الكائنات من نفس النوع (المزينين).
- نمط المزيّن يستخدم كثيرًا في فئات Java IO، مثل FileReader، BufferedReader وما إلى ذلك.
Source:
https://www.digitalocean.com/community/tutorials/decorator-design-pattern-in-java-example