المزيد عن المشكلة
أولاً، سنقوم بمحاكاة سلوك النظام القديم. لنفترض أنه يولد أعذارًا للتأخر عن العمل أو المدرسة. وللقيام بذلك، فهو يحتوي علىExcuse
واجهة generateExcuse()
وطرق . likeExcuse()
dislikeExcuse()
public interface Excuse {
String generateExcuse();
void likeExcuse(String excuse);
void dislikeExcuse(String excuse);
}
يطبق الفصل WorkExcuse
هذه الواجهة:
public class WorkExcuse implements Excuse {
private String[] excuses = {"in an incredible confluence of circumstances, I ran out of hot water and had to wait until sunlight, focused using a magnifying glass, heated a mug of water so that I could wash.",
"the artificial intelligence in my alarm clock failed me, waking me up an hour earlier than normal. Because it is winter, I thought it was still nighttime and I fell back asleep. Everything after that is a bit hazy.",
"my pre-holiday mood slows metabolic processes in my body, leading to depression and insomnia."};
private String [] apologies = {"This will not happen again, of course. I'm very sorry.", "I apologize for my unprofessional behavior.", "There is no excuse for my actions. I am not worthy of this position."};
@Override
public String generateExcuse() { // Randomly select an excuse from the array
String result = "I was late today because " + excuses[(int) Math.round(Math.random() + 1)] + "\\n" +
apologies[(int) Math.round(Math.random() + 1)];
return result;
}
@Override
public void likeExcuse(String excuse) {
// Duplicate the element in the array so that its chances of being chosen are higher
}
@Override
public void dislikeExcuse(String excuse) {
// Remove the item from the array
}
}
دعونا نختبر مثالنا:
Excuse excuse = new WorkExcuse();
System.out.println(excuse.generateExcuse());
انتاج:
"I was late today because my pre-holiday mood slows metabolic processes in my body, leading to depression and insomnia.
I apologize for my unprofessional behavior.
تخيل الآن أنك أطلقت خدمة تقديم الأعذار، وجمعت إحصائيات، ولاحظت أن معظم مستخدميك هم طلاب جامعيون. لخدمة هذه المجموعة بشكل أفضل، طلبت من مطور آخر إنشاء نظام يقوم بإنشاء أعذار مخصصة لطلاب الجامعة. أجرى فريق التطوير أبحاث السوق، ورتب الأعذار، وربط بعض الذكاء الاصطناعي، ودمج الخدمة مع تقارير حركة المرور، وتقارير الطقس، وما إلى ذلك. الآن لديك مكتبة لتوليد الأعذار لطلاب الجامعة، ولكن لها واجهة مختلفة: StudentExcuse
.
public interface StudentExcuse {
String generateExcuse();
void dislikeExcuse(String excuse);
}
تحتوي هذه الواجهة على طريقتين: generateExcuse
التي تولد عذرًا، و dislikeExcuse
التي تمنع ظهور العذر مرة أخرى في المستقبل. لا يمكن تحرير مكتبة الطرف الثالث، أي لا يمكنك تغيير كود المصدر الخاص بها. ما لدينا الآن هو نظام مكون من فئتين ينفذان الواجهة Excuse
، ومكتبة بها SuperStudentExcuse
فئتان تنفذان الواجهة StudentExcuse
:
public class SuperStudentExcuse implements StudentExcuse {
@Override
public String generateExcuse() {
// Logic for the new functionality
return "An incredible excuse adapted to the current weather conditions, traffic jams, or delays in public transport schedules.";
}
@Override
public void dislikeExcuse(String excuse) {
// Adds the reason to a blacklist
}
}
لا يمكن تغيير الرمز. يبدو التسلسل الهرمي الحالي للفئة كما يلي: هذا الإصدار من النظام يعمل فقط مع واجهة Excuse. لا يمكنك إعادة كتابة التعليمات البرمجية: في تطبيق كبير، قد يصبح إجراء مثل هذه التغييرات عملية طويلة أو يعطل منطق التطبيق. يمكننا تقديم واجهة أساسية وتوسيع التسلسل الهرمي: للقيام بذلك، علينا إعادة تسمية الواجهة Excuse
. لكن التسلسل الهرمي الإضافي غير مرغوب فيه في التطبيقات الجادة: فإدخال عنصر جذر مشترك يكسر البنية. يجب عليك تنفيذ فئة وسيطة تسمح لنا باستخدام كل من الوظائف الجديدة والقديمة بأقل قدر من الخسائر. باختصار، أنت بحاجة إلى محول .
المبدأ الكامن وراء نمط المحول
المحول هو كائن وسيط يسمح باستدعاءات الأسلوب لكائن واحد أن يفهمها كائن آخر. لننفذ محولًا لمثالنا ونسميهMiddleware
. يجب أن يقوم المحول الخاص بنا بتنفيذ واجهة متوافقة مع أحد الكائنات. فليكن Excuse
. يسمح هذا Middleware
باستدعاء أساليب الكائن الأول. Middleware
يستقبل المكالمات ويعيد توجيهها بطريقة متوافقة إلى الكائن الثاني. وإليك التنفيذ Middleware
مع الأساليب generateExcuse
والأساليب dislikeExcuse
:
public class Middleware implements Excuse { // 1. Middleware becomes compatible with WorkExcuse objects via the Excuse interface
private StudentExcuse superStudentExcuse;
public Middleware(StudentExcuse excuse) { // 2. Get a reference to the object being adapted
this.superStudentExcuse = excuse;
}
@Override
public String generateExcuse() {
return superStudentExcuse.generateExcuse(); // 3. The adapter implements an interface method
}
@Override
public void dislikeExcuse(String excuse) {
// The method first adds the excuse to the blacklist,
// Then passes it to the dislikeExcuse method of the superStudentExcuse object.
}
// The likeExcuse method will appear later
}
الاختبار (في رمز العميل):
public class Test {
public static void main(String[] args) {
Excuse excuse = new WorkExcuse(); // We create objects of the classes
StudentExcuse newExcuse = new SuperStudentExcuse(); // that must be compatible.
System.out.println("An ordinary excuse for an employee:");
System.out.println(excuse.generateExcuse());
System.out.println("\n");
Excuse adaptedStudentExcuse = new Middleware(newExcuse); // Wrap the new functionality in the adapter object
System.out.println("Using new functionality with the adapter:");
System.out.println(adaptedStudentExcuse.generateExcuse()); // The adapter calls the adapted method
}
}
انتاج:
An ordinary excuse for an employee:
I was late today because my pre-holiday mood slows metabolic processes in my body, leading to depression and insomnia.
There is no excuse for my actions. I am not worthy of this position.
Using new functionality with the adapter:
عذر لا يصدق يتكيف مع الظروف الجوية الحالية أو الاختناقات المرورية أو التأخير في مواعيد وسائل النقل العام. تقوم الطريقة generateExcuse
ببساطة بتمرير الاستدعاء إلى كائن آخر، دون أي تغييرات إضافية. تطلبت الطريقة dislikeExcuse
منا أن نضع العذر في القائمة السوداء أولاً. تعد القدرة على إجراء معالجة وسيطة للبيانات سببًا وراء حب الناس لنمط المحول. ولكن ماذا عن likeExcuse
الطريقة التي تعد جزءًا من Excuse
الواجهة ولكنها ليست جزءًا من StudentExcuse
الواجهة؟ الوظيفة الجديدة لا تدعم هذه العملية. تم UnsupportedOperationException
اختراعه لهذه الحالة. يتم طرحه إذا كانت العملية المطلوبة غير مدعومة. دعونا استخدامه. هذه هي الطريقة التي Middleware
يبدو بها التنفيذ الجديد للفئة:
public class Middleware implements Excuse {
private StudentExcuse superStudentExcuse;
public Middleware(StudentExcuse excuse) {
this.superStudentExcuse = excuse;
}
@Override
public String generateExcuse() {
return superStudentExcuse.generateExcuse();
}
@Override
public void likeExcuse(String excuse) {
throw new UnsupportedOperationException("The likeExcuse method is not supported by the new functionality");
}
@Override
public void dislikeExcuse(String excuse) {
// The method accesses a database to fetch additional information,
// and then passes it to the superStudentExcuse object's dislikeExcuse method.
}
}
للوهلة الأولى، لا يبدو هذا الحل جيدًا جدًا، لكن تقليد الوظيفة قد يؤدي إلى تعقيد الموقف. إذا كان العميل مهتمًا، وكان المحول موثقًا جيدًا، فإن هذا الحل يكون مقبولاً.
متى تستخدم المحول
-
عندما تحتاج إلى استخدام فئة خارجية، ولكن واجهتها غير متوافقة مع التطبيق الرئيسي. يوضح المثال أعلاه كيفية إنشاء كائن محول يقوم بتجميع المكالمات بتنسيق يمكن للكائن الهدف فهمه.
-
عندما تحتاج العديد من الفئات الفرعية الموجودة إلى بعض الوظائف المشتركة. بدلاً من إنشاء فئات فرعية إضافية (مما سيؤدي إلى تكرار التعليمات البرمجية)، فمن الأفضل استخدام محول.
المميزات والعيوب
الميزة: يخفي المحول عن العميل تفاصيل معالجة الطلبات من كائن إلى آخر. لا يفكر رمز العميل في تنسيق البيانات أو التعامل مع المكالمات إلى الطريقة المستهدفة. إنه معقد للغاية والمبرمجون كسالى :) العيب: قاعدة كود المشروع معقدة بسبب الفئات الإضافية. إذا كان لديك الكثير من الواجهات غير المتوافقة، فقد يصبح عدد الفئات الإضافية غير قابل للإدارة.لا تخلط بين المحول والواجهة أو الديكور
من خلال الفحص السطحي فقط، يمكن الخلط بين المحول وأنماط الواجهة والديكور. الفرق بين المحول والواجهة هو أن الواجهة تقدم واجهة جديدة وتغلف النظام الفرعي بأكمله. ومصمم الديكور، على عكس المحول، يغير الكائن نفسه بدلاً من الواجهة.خوارزمية خطوة بخطوة
-
أولاً، تأكد من أن لديك مشكلة يمكن لهذا النمط حلها.
-
حدد واجهة العميل التي سيتم استخدامها للتفاعل بشكل غير مباشر مع الكائنات غير المتوافقة.
-
اجعل فئة المحول ترث الواجهة المحددة في الخطوة السابقة.
-
في فئة المحول، قم بإنشاء حقل لتخزين مرجع إلى كائن المحول. يتم تمرير هذا المرجع إلى المنشئ.
-
تنفيذ كافة أساليب واجهة العميل في المحول. قد تكون الطريقة:
-
تمرير المكالمات دون إجراء أي تغييرات
-
تعديل البيانات أو استكمالها، وزيادة/تقليل عدد الاستدعاءات للطريقة المستهدفة، وما إلى ذلك.
-
في الحالات القصوى، إذا ظلت طريقة معينة غير متوافقة، فقم بطرح UnsupportedOperationException. يجب توثيق العمليات غير المدعومة بدقة.
-
-
إذا كان التطبيق يستخدم فئة المحول فقط من خلال واجهة العميل (كما في المثال أعلاه)، فيمكن توسيع المحول بسهولة في المستقبل.
GO TO FULL VERSION