يبدأ كتاب الدكتور يان هونغ "JAVA والأنماط" بوصف نمط الوسيط:
نمط الوسيط هو نمط سلوك الكائنات. يختتم نمط الوسيط الطريقة التي تتفاعل بها مجموعة من الكائنات بحيث لا تضطر إلى الإشارة إلى بعضها البعض بشكل صريح. وهذا يسمح لهم أن يقترنوا بشكل فضفاض. عندما تتغير التفاعلات بين بعض هذه الكائنات، فإن ذلك لا يؤثر مباشرة على التفاعلات بين الكائنات الأخرى. وهذا يضمن أن هذه التفاعلات يمكن أن تختلف بشكل مستقل عن بعضها البعض.
لماذا هناك حاجة إلى وسيط
كما هو موضح في الشكل أدناه، يوجد عدد كبير من الكائنات في هذا المخطط التخطيطي، ويمكن أن تؤثر هذه الكائنات على كائنات أخرى وتتأثر بكائنات أخرى، لذلك يطلق عليها غالبًا اسم كائنات الزملاء. يشكل هؤلاء الزملاء سلوك النظام من خلال تفاعلهم مع بعضهم البعض. كما يتبين من الشكل، يحتاج كل كائن تقريبًا إلى التفاعل مع كائنات أخرى، ويتجلى هذا التفاعل في صورة اقتران مباشر بين كائن وكائن آخر. هذا هو نظام overcoupled.
ومن خلال إدخال الكائن الوسيط (Mediator)، يمكن تحويل البنية الشبكية للنظام إلى بنية نجمية يتمركز حولها الوسيط، كما هو موضح في الشكل أدناه. في هذا الهيكل النجمي، لم يعد الكائن الزميل يتفاعل مع كائن آخر من خلال اتصال مباشر؛ وبدلاً من ذلك، يتفاعل مع كائن آخر من خلال كائن وسيط. يضمن وجود الكائن الوسيط استقرار بنية الكائن، أي أن بنية النظام لن تسبب الكثير من أعمال التعديل بسبب إدخال كائنات جديدة.
يمكن للتصميم الجيد الموجه للكائنات أن يزيد التعاون ويقلل الاقتران بين الكائنات. يقوم التصميم المدروس جيدًا بتقسيم النظام إلى مجموعة من كائنات زملاء العمل المتعاونة، ثم يمنح كل كائن من عناصر زملاء العمل مسؤوليات فريدة ويقوم بتكوين العلاقات التعاونية بينهم بشكل مناسب حتى يتمكنوا من العمل معًا.
إذا لم يكن هناك اللوحة الأم
كما نعلم جميعًا، يتم التفاعل بين الملحقات المختلفة في الكمبيوتر بشكل أساسي من خلال اللوحة الأم. إذا لم تكن هناك لوحة أم في الكمبيوتر، فيجب أن تتفاعل الملحقات المختلفة مع بعضها البعض بمفردها لنقل البيانات إلى بعضها البعض. ولأن واجهات كل ملحق مختلفة، عند التفاعل مع بعضها البعض، يجب تحويل واجهة البيانات لتتناسب.
لحسن الحظ، مع اللوحة الأم، يكتمل تفاعل الملحقات المختلفة بشكل كامل من خلال اللوحة الأم، وكل ملحق يحتاج فقط إلى التفاعل مع اللوحة الأم، وتعرف اللوحة الأم كيفية التعامل مع جميع الملحقات، مما يجعل الأمر أسهل بكثير.
هيكل نمط الوسيط
يظهر أدناه رسم تخطيطي لفئة نمط الوسيط:
يتضمن وضع الوسيط الأدوار التالية:
● دور الوسيط المجرد (الوسيط): حدد الواجهة من كائن الزميل إلى كائن الوسيط، حيث تكون الطريقة الرئيسية هي طريقة حدث واحدة (أو أكثر).
● دور الوسيط الملموس (ConcreteMediator): ينفذ طريقة الحدث المعلن عنها بواسطة الوسيط المجرد. الوسيط المحدد على علم بجميع فئات الزملاء المحددة وهو مسؤول عن تنسيق التفاعل بين كل كائن زميل على وجه التحديد.
● دور فئة الزميل الملخص (الزميل): يحدد الواجهة من الوسيط إلى كائن الزميل. الكائنات الزميلة تعرف فقط عن الوسيط وليس الكائنات الجماعية الأخرى.
● دور الزميل الملموس: ترث جميع فئات الزملاء الملموسة من فئة الزملاء المجردة. لتنفيذ أعمالك الخاصة، عندما تحتاج إلى التواصل مع زملاء آخرين، تواصل مع الوسيط الذي يتولى الأمر، وسيكون الوسيط مسؤولاً عن التفاعل مع الزملاء الآخرين.
كود المصدر
رمز نسخة فئة الوسيط الملخص هو كما يلي:
وسيط الواجهة العامة {
/**
* يقوم كائن الزميل بإعلام طريقة الوسيط عند تغييره.
* دع الوسيط يكون مسؤولاً عن التفاعلات المقابلة مع كائنات الزملاء الأخرى
*/
تم تغيير الفراغ العام (الزميل ج)؛
}
فئة وسيط ملموسة
انسخ رمز الكود كما يلي:
الطبقة العامة ConcreteMediator تنفذ الوسيط {
// عقد والحفاظ على الزميل أ
خاص ملموسةزميلزميلأ؛
// عقد والحفاظ على الزميل ب
خاص ConcreteCollegueB زميل B؛
مجموعة الفراغ العامةColleagueA(ConcreteColleagueA ColleagueA) {
this.colleagueA = CollegueA;
}
مجموعة الفراغ العامةColleagueB(ConcreteColleagueB ColleagueB) {
this.colleagueB = CollageB;
}
@تجاوز
تم تغيير الفراغ العام (الزميل ج) {
/**
* لقد تغيرت فئة معينة من الزملاء، وعادة ما تتطلب التفاعل مع الزملاء الآخرين
* تنسيق كائنات الزميل المقابلة على وجه التحديد لتحقيق السلوك التعاوني
*/
}
}
فئة زميل مجردة
انسخ رمز الكود كما يلي:
زميل فئة مجردة عامة {
// احتفظ بكائن وسيط
الوسيط الخاص؛
/**
* منشئ
*/
الزميل العام(الوسيط الوسيط){
this.mediator = mediator;
}
/**
* احصل على كائن الوسيط المطابق لفئة الزميل الحالية
*/
الوسيط العام getMediator() {
وسيط العودة
}
}
الكود المحدد لنسخ نفس الفصل هو كما يلي:
الطبقة العامة ConcreteColleagueA تمتد الزميل {
عامة ConcreteCollegueA(الوسيط الوسيط) {
سوبر (الوسيط)؛
}
/**
* الإشارة إلى طرق تنفيذ عمليات معينة
*/
عملية الفراغ العام () {
// قم بإعلام كائن الوسيط عندما تحتاج إلى التواصل مع زملاء آخرين
getMediator().changed(this);
}
}
انسخ رمز الكود كما يلي:
الطبقة العامة ConcreteColleagueB تمتد الزميل {
عامة ConcreteColleagueB(الوسيط الوسيط) {
سوبر (الوسيط)؛
}
/**
* الإشارة إلى طرق تنفيذ عمليات معينة
*/
عملية الفراغ العام () {
// قم بإعلام كائن الوسيط عندما تحتاج إلى التواصل مع زملاء آخرين
getMediator().changed(this);
}
}
استخدم جهاز الكمبيوتر الخاص بك لمشاهدة الأفلام
في الحياة اليومية، غالبًا ما نستخدم أجهزة الكمبيوتر لمشاهدة الأفلام، دعونا نصف هذه العملية، بعد التبسيط، نفترض أنه ستكون هناك العملية التفاعلية التالية:
(1) أولاً، يحتاج محرك الأقراص الضوئية إلى قراءة البيانات الموجودة على القرص الضوئي، ثم إخبار اللوحة الأم بأن حالته قد تغيرت.
(2) تحصل اللوحة الأم على البيانات من محرك الأقراص الضوئية وتقوم بتسليم البيانات إلى وحدة المعالجة المركزية لتحليلها ومعالجتها.
(3) بعد انتهاء وحدة المعالجة المركزية من المعالجة، تقوم بتقسيم البيانات إلى بيانات فيديو وبيانات صوتية، وتخطر اللوحة الأم بأنها انتهت من المعالجة.
(4) تحصل اللوحة الأم على البيانات التي تتم معالجتها بواسطة وحدة المعالجة المركزية وتمرر البيانات إلى بطاقة الرسومات وبطاقة الصوت على التوالي لعرض الفيديو وإصدار الأصوات.
لتنفيذ المثال باستخدام نمط الوسيط، من الضروري التمييز بين الكائنات الزميلة والكائنات الوسيطة. من الواضح أن اللوحة الأم هي الوسيط، والملحقات مثل محركات الأقراص الضوئية وبطاقات الصوت ووحدات المعالجة المركزية وبطاقات الرسومات جميعها زملاء.
كود المصدر
رمز نسخة فئة الزميل الملخص هو كما يلي:
زميل فئة عامة مجردة {
// احتفظ بكائن وسيط
الوسيط الخاص؛
/**
* منشئ
*/
الزميل العام(الوسيط الوسيط){
this.mediator = mediator;
}
/**
* احصل على كائن الوسيط المطابق لفئة الزميل الحالية
*/
الوسيط العام getMediator() {
وسيط العودة
}
}
فئة الزملاء - رمز نسخ محرك الأقراص الضوئية هو كما يلي:
يمتد CDDriver من الفئة العامة للزميل {
// قراءة البيانات من محرك الأقراص الضوئية
بيانات السلسلة الخاصة = ""؛
/**
* منشئ
*/
محرك الأقراص العامة (الوسيط الوسيط) {
سوبر (الوسيط)؛
}
/**
* الحصول على البيانات المقروءة من القرص
*/
سلسلة عامة getData () {
إرجاع البيانات؛
}
/**
* قراءة القرص المضغوط
*/
قراءة الفراغ العام () {
// قبل الفاصلة هي البيانات المعروضة في الفيديو، وبعد الفاصلة هو الصوت
this.data = "ون بيس، أنا مصمم على أن أصبح ملك القراصنة";
// أبلغ اللوحة الأم بأن حالتها قد تغيرت
getMediator().changed(this);
}
}
فئة الزملاء —— وحدة المعالجة المركزية
انسخ رمز الكود كما يلي:
تعمل وحدة المعالجة المركزية للفئة العامة على توسيع الزميل {
// بيانات الفيديو المتحللة
سلسلة خاصة videoData = "";
// بيانات الصوت المتحللة
سلسلة صوتية خاصة = "";
/**
* منشئ
*/
وحدة المعالجة المركزية العامة (الوسيط الوسيط) {
سوبر (الوسيط)؛
}
/**
* احصل على بيانات الفيديو المتحللة
*/
سلسلة عامة getVideoData () {
إرجاع بيانات الفيديو؛
}
/**
* الحصول على البيانات الصوتية المتحللة
*/
سلسلة عامة getSoundData () {
إرجاع بيانات الصوت؛
}
/**
* معالجة البيانات وتقسيم البيانات إلى بيانات صوتية وبيانات فيديو
*/
تنفيذ الفراغ العام (بيانات السلسلة) {
// تحلل البيانات. الجزء الأمامي عبارة عن بيانات فيديو والجزء الخلفي عبارة عن بيانات صوتية.
String[] array = data.split("،");
this.videoData = array[0];
this.soundData = array[1];
// أبلغ اللوحة الأم بأن وحدة المعالجة المركزية قد أكملت العمل
getMediator().changed(this);
}
}
فئة الزملاء - رمز كود نسخ بطاقة الرسومات هو كما يلي:
الطبقة العامة VideoCard تمتد الزميل {
/**
* منشئ
*/
بطاقة الفيديو العامة (الوسيط الوسيط) {
سوبر (الوسيط)؛
}
/**
* عرض بيانات الفيديو
*/
showData الفراغ العام (بيانات السلسلة) {
System.out.println("أنت تشاهد:" + data);
}
}
فئة الزملاء - كود نسخ بطاقة الصوت هو كما يلي:
الطبقة العامة SoundCard تمتد الزميل {
/**
* منشئ
*/
بطاقة الصوت العامة (الوسيط الوسيط) {
سوبر (الوسيط)؛
}
/**
* إصدار أصوات بناءً على البيانات الصوتية
*/
بيانات الصوت باطلة عامة (بيانات السلسلة) {
System.out.println("التعليق الصوتي: " + البيانات);
}
}
رمز نسخة فئة الوسيط الملخص هو كما يلي:
وسيط الواجهة العامة {
/**
* يقوم كائن الزميل بإعلام طريقة الوسيط عند تغييره.
* دع الوسيط يكون مسؤولاً عن التفاعلات المقابلة مع كائنات الزملاء الأخرى
*/
تم تغيير الفراغ العام (الزميل ج)؛
}
رمز نسخة فئة الوسيط المحدد هو كما يلي:
الطبقة العامة MainBoard تنفذ الوسيط {
// بحاجة إلى معرفة فئة الزميل للتفاعل معها - فئة محرك الأقراص الضوئية
برنامج CDDriver الخاص cdDriver = null;
// بحاجة إلى معرفة فئة الزميل للتفاعل مع فئة وحدة المعالجة المركزية
وحدة المعالجة المركزية الخاصة cpu = null;
// بحاجة إلى معرفة فئة الزملاء للتفاعل معهم - فئة بطاقة الرسومات
بطاقة فيديو خاصة videoCard = null;
// بحاجة إلى معرفة فئة الزملاء للتفاعل معهم - فئة بطاقة الصوت
بطاقة الصوت الخاصة soundCard = null;
مجموعة الفراغ العام CdDriver (CDDriver cdDriver) {
this.cdDriver = cdDriver;
}
مجموعة الفراغ العام (وحدة المعالجة المركزية وحدة المعالجة المركزية) {
this.cpu = cpu;
}
مجموعة الفراغ العامةVideoCard(VideoCard videoCard) {
this.videoCard = videoCard;
}
مجموعة باطلة عامةSoundCard(SoundCard soundCard) {
this.soundCard = soundCard;
}
@تجاوز
تم تغيير الفراغ العام (الزميل ج) {
إذا (ج مثيل CDDriver) {
// يشير إلى أن محرك الأقراص الضوئية قد قرأ البيانات
this.opeCDDriverReadData((CDDriver)c);
}else if(c مثيل لوحدة المعالجة المركزية){
this.opeCPU((CPU)c);
}
}
/**
* تعامل مع التفاعل مع الكائنات الأخرى بعد أن يقرأ محرك الأقراص الضوئية البيانات
*/
opeCDDriverReadData باطلة خاصة (CDDriver cd) {
// قم أولاً بقراءة البيانات بواسطة محرك الأقراص الضوئية
بيانات السلسلة = cd.getData();
// انقل هذه البيانات إلى وحدة المعالجة المركزية للمعالجة
cpu.executeData(data);
}
/**
* التعامل مع التفاعل بين وحدة المعالجة المركزية والكائنات الأخرى بعد معالجة البيانات
*/
وحدة المعالجة المركزية الفارغة الخاصة (وحدة المعالجة المركزية وحدة المعالجة المركزية) {
// احصل على البيانات التي تتم معالجتها بواسطة وحدة المعالجة المركزية أولاً
String videoData = cpu.getVideoData();
String soundData = cpu.getSoundData();
// انقل هذه البيانات إلى بطاقة الرسومات وبطاقة الصوت للعرض
videoCard.showData(videoData);
soundCard.soundData(soundData);
}
}
رمز نسخة فئة العميل كما يلي:
عميل الطبقة العامة {
public static void main(String[] args) {
// إنشاء وسيط - اللوحة الأم
وسيط اللوحة الرئيسية = اللوحة الرئيسية الجديدة ()؛
// إنشاء فئة زميل
CDDriver cd = new CDDriver(mediator);
وحدة المعالجة المركزية وحدة المعالجة المركزية = وحدة المعالجة المركزية الجديدة (الوسيط)؛
VideoCard vc = new VideoCard(mediator);
SoundCard sc = new SoundCard(mediator);
// دع الوسيط يعرف جميع الزملاء
mediator.setCdDriver(cd);
mediator.setCpu(cpu);
mediator.setVideoCard(vc);
mediator.setSoundCard(sc);
// ابدأ بمشاهدة الفيلم، ضع القرص في محرك الأقراص الضوئية، وسيبدأ محرك الأقراص الضوئية في قراءة القرص
cd.readCD();
}
}
نتائج التشغيل هي كما يلي:
مزايا نمط الوسيط
● اقتران فضفاض
يقوم نمط الوسيط بتغليف التفاعلات بين كائنات زميلة متعددة في كائن الوسيط، وبالتالي اقتران كائنات الزميل بشكل فضفاض وتحقيق التبعيات التكميلية بشكل أساسي. وبهذه الطريقة، يمكن تغيير الأشياء الزميلة وإعادة استخدامها بشكل مستقل، بدلاً من "تحريك مكان واحد والتأثير على الجسم كله" كما كان من قبل.
● التحكم المركزي في التفاعلات
يتم تغليف تفاعلات كائنات الزملاء المتعددة في كائن الوسيط للإدارة المركزية، بحيث عندما تتغير هذه السلوكيات التفاعلية، تحتاج فقط إلى تعديل كائن الوسيط، بالطبع، إذا كان نظامًا مكتملًا بالفعل، فقم بتوسيع كائن الوسيط. ولا يحتاج كل فئة زميل إلى تعديل.
● يصبح "من متعدد إلى متعدد" واحدًا إلى متعدد
عندما لا يتم استخدام نمط الوسيط، تكون العلاقة بين الكائنات الزميلة عادةً متعددة إلى متعددة. بعد تقديم الكائن الوسيط، تصبح العلاقة بين الكائن الوسيط والكائن الزميل عادةً ثنائية الاتجاه. مما يجعل العلاقة بين الكائنات أسهل في الفهم والتنفيذ.
عيوب نمط الوسيط
أحد العوائق المحتملة لنموذج الوسيط هو الإفراط في المركزية. إذا كان تفاعل الكائنات الزميلة كبيرًا جدًا ومعقدًا، فعندما تتركز كل هذه التعقيدات على الوسيط، يصبح الكائن الوسيط معقدًا للغاية ويصعب إدارته وصيانته.