java ماهو - كيف تختلف أنماط Proxy و Decorator و Adapter و Bridge؟





بروكسي مجاني (12)


إنها متشابهة تمامًا ، والخطوط بينهما رمادية تمامًا. أقترح عليك قراءة إدخالات نمط الوكيل و Decorator في c2 wiki.

الإدخالات والمناقشات هناك واسعة للغاية ، وترتبط أيضًا بمقالات أخرى ذات صلة. بالمناسبة ، فإن c2 wiki ممتاز عندما تتساءل عن الفروق الدقيقة بين الأنماط المختلفة.

لتلخيص إدخالات c2 ، أود أن أقول أن الديكور يضيف / يغير من السلوك ، لكن الوكيل له علاقة أكبر بالسيطرة على الوصول (مثل التثبيت الفوري ، الوصول عن بعد ، الأمن ، الخ). ولكن كما قلت ، فإن الخطوط بينهما رمادية ، وأرى إشارات إلى وكلاء يمكن النظر إليهم بسهولة على أنهم مصممون والعكس صحيح.

كنت أنظر إلى نمط الوكيل ، ويبدو لي الكثير مثل أنماط الديكور والمحول والجسور. هل أسيء فهم شيء ما؟ ماهو الفرق؟ لماذا أستخدم نمط الوكيل مقابل الآخرين؟ كيف استخدمتها في الماضي في مشاريع العالم الحقيقي؟




أنا استخدم ذلك في كثير من الأحيان عند استهلاك خدمات الويب. من المحتمل أن يتم إعادة تسمية نمط الوكيل إلى شيء أكثر واقعية ، مثل "نمط التفاف". كما أن لدي مكتبة هي بروكسي إلى MS Excel ، وهو ما يجعل من السهل جدا أتمتة Excel ، دون الحاجة إلى القلق بشأن تفاصيل الخلفية مثل ما يتم تثبيت الإصدار (إن وجد).




أود أن أضيف أمثلة إلى جواب بيل كاروينج (وهو أمر رائع.) وأضيف أيضا بعض الاختلافات الرئيسية في التنفيذ ، التي أشعر أنها مفقودة.

الأجزاء المقتبسة من إجابة [ https://.com/a/350471/1984346] (Bill Karwing)

وكيل ، ديكور ، محول ، وجسر هي كل الاختلافات على "التفاف" فئة. لكن استخداماتها مختلفة.

  • يمكن استخدام الوكيل عند الرغبة في إنشاء كائن كسلس أو إخفاء حقيقة أنك تتصل بخدمة بعيدة أو تتحكم في الوصول إلى الكائن.

يجب تنفيذ ProxyClass و ObjectClass التي يتم proxied نفس الواجهة ، بحيث تكون قابلة للتبادل

سبيل المثال - كائن مكلفة الوكيل

class ProxyHumanGenome implements GenomeInterface  {
    private $humanGenome = NULL; 

    // humanGenome class is not instantiated at construct time
    function __construct() {
    }

    function getGenomeCount() {
        if (NULL == $this->humanGenome) {
            $this->instantiateGenomeClass(); 
        }
        return $this->humanGenome->getGenomeCount();
    }
} 
class HumanGenome implement GenomeInterface { ... }
  • يسمى الديكور أيضًا بـ "Smart Proxy". يتم استخدام هذا عندما تريد إضافة وظيفة إلى كائن ، ولكن ليس عن طريق توسيع نوع ذلك الكائن. هذا يسمح لك أن تفعل ذلك في وقت التشغيل.

يجب أن تقوم DecoratorClass (يمكن) بتنفيذ واجهة موسعة من ObjectClass. لذلك يمكن استبدال ObjectClass بواسطة DecoratorClass ، ولكن ليس العكس.

مثال - إضافة وظيفة إضافة

class DecoratorHumanGenome implements CheckGenomeInterface  {

    // ... same code as previous example

    // added functionality
    public function isComplete() {
        $this->humanGenome->getCount >= 21000
    }
}

interface CheckGenomeInterface extends GenomeInterface {

    public function isComplete();

}

class HumanGenome implement GenomeInterface { ... }
  • يتم استخدام المحول عندما يكون لديك واجهة مجردة ، وترغب في تعيين هذه الواجهة إلى كائن آخر له دور وظيفي مماثل ، ولكن واجهة مختلفة.

فروق التصغير وكيل ، ديكور ، محول

يوفر محول واجهة مختلفة لموضوعه. يوفر الوكيل نفس الواجهة. يوفر Decorator واجهة محسنة.

  • الجسر مشابه جدًا للمهايئ ، لكننا نسميه Bridge عند تعريف كل من الواجهة المجردة والتنفيذ الأساسي. أي أنك لا تتكيف مع بعض التعليمة البرمجية القديمة أو الخارجية ، فأنت مصمم جميع التعليمات البرمجية ولكنك تحتاج إلى أن تتمكن من تبديل التطبيقات المختلفة.

  • الواجهة واجهة مستوى أعلى (قراءة: أبسط) إلى نظام فرعي من فئة واحدة أو أكثر. افترض أن لديك مفهومًا معقدًا يتطلب أن تمثِّل كائنات متعددة. إن إجراء تغييرات على مجموعة الكائنات هذه أمر مربك ، لأنك لا تعرف دائمًا أي كائن لديه الطريقة التي تحتاج إلى الاتصال بها. هذا هو الوقت المناسب لكتابة الواجهة التي توفر أساليب عالية المستوى لجميع العمليات المعقدة التي يمكنك القيام بها لجمع الأشياء. مثال: نموذج المجال لقسم المدرسة ، مع طرق مثل countStudents() ، reportAttendance() ، assignSubstituteTeacher() ، وما إلى ذلك.

معظم المعلومات في هذه الإجابة هي من https://sourcemaking.com/design_patterns ، والتي أوصي بها كمصدر ممتاز لأنماط التصميم.




نمط التصميم ليس الرياضيات ، بل هو مزيج من الفن وهندسة البرمجيات. لا يوجد شيء مثل هذا المطلب لديك لاستخدام الوكيل ، الجسر إلخ. يتم إنشاء أنماط التصميم لحل المشاكل. إذا كنت تتوقع مشكلة في التصميم ، فاستخدمها. استنادًا إلى الخبرة ، ستعرف عن مشكلة محددة ، نمط الاستخدام. إذا كنت جيدًا في مبادئ التصميم المتين ، فستكون قد نفذت نمط التصميم دون أن تعرف أنه نمط. المثال الشائع هو أنماط التصنيع والمصنع

ومن ثم التركيز أكثر على مبادئ desighn صلبة ، مبادئ الترميز النظيف و ttd




لقد أوضحت جميع الإجابات الجيدة من الخبراء ما الذي يرمز إليه كل نمط.

سأقوم بتزيين النقاط الرئيسية.

الديكور:

  1. إضافة السلوك إلى الكائن في وقت التشغيل . الميراث هو المفتاح لتحقيق هذه الوظيفة ، والتي هي ميزة وعيب هذا النمط.
  2. إنه يعدل سلوك الواجهة.

على سبيل المثال (مع تسلسل): فئات حزم java.io ذات الصلة java.io

FileOutputStream fos1 = new FileOutputStream("data1.txt");  
ObjectOutputStream out1 = new ObjectOutputStream(fos1);

الوكيل:

  1. استخدامه لتهيئة كسول ، وتحسين الأداء عن طريق تخزين الكائن المؤقت والتحكم في الوصول إلى العميل / المتصل . قد توفر سلوك بديل أو استدعاء كائن حقيقي. خلال هذه العملية ، قد يقوم بإنشاء كائن جديد.
  2. على عكس الديكور ، الذي يسمح بتسلسل الكائنات ، لا يسمح الوكيل بالتسلسل.

على سبيل المثال: فئات الحزمة java.rmi .

مشترك كهربائي:

  1. ويسمح بواجهتين غير مترابطتين للعمل معًا من خلال كائنات مختلفة ، وقد يلعبان نفس الدور.
  2. يعدل الواجهة الأصلية .

على سبيل المثال ، java.io.InputStreamReader (يعرض InputStream )

جسر:

  1. إنه يسمح لكل من التجريدات والتطبيقات بالتغيير بشكل مستقل .
  2. يستخدم التكوين على الميراث .

مثل فئات التجميع في java.util . List تنفيذها من قبل ArrayList .

ملاحظات رئيسية:

  1. يوفر محول واجهة مختلفة لموضوعه. يوفر الوكيل نفس الواجهة. يوفر Decorator واجهة محسنة.
  2. يقوم المحول بتغيير واجهة كائن ما ، يقوم Decorator بتحسين مسؤوليات الكائن.
  3. للديكور والوكسيبي أغراض مختلفة لكن هياكل متشابهة
  4. يجعل المحول الأشياء تعمل بعد تصميمها ؛ الجسر يجعلها تعمل قبل أن تكون.
  5. تم تصميم Bridge مسبقًا للسماح للتجريد والتنفيذ بالتغيير بشكل مستقل. يتم إعادة تهيئة المحول لإنشاء فئات غير مرتبطة ببعضها البعض
  6. تم تصميم Decorator ليسمح لك بإضافة مسؤوليات إلى الكائنات دون تصنيفها.

إلقاء نظرة على أسئلة / مقالات SE كبيرة بشأن أمثلة من أنماط التصميم المختلفة

متى تستخدم نمط الديكور؟

متى تستخدم نمط الجسر؟ كيف تختلف عن نمط محول؟

الاختلافات بين وكيل ونمط ديكور




وكما تقول إجابة بيل ، فإن حالات استخدامها تختلف .

لذلك هي هياكلها.

  • كل من الوكيل و الديكور لهما نفس الواجهة لأنواعهما الملتفة ، لكن الوكيل يقوم بإنشاء مثيل تحت غطاء المحرك ، في حين يأخذ الديكور مثالاً في المنشئ.

  • يتميز كل من المهايئ والواجهات بواجهة مختلفة عن تلك التي تلتف بها. لكن المحول مشتق من واجهة موجودة ، بينما تخلق الواجهة واجهة جديدة.

  • الجسر والمحول كلاهما يشير إلى نوع موجود. لكن الجسر سوف يشير إلى نوع تجريدي ، وقد يشير المحول إلى نوع ملموس. سيسمح لك الجسر بإقران التطبيق في وقت التشغيل ، بينما لا يقوم المحول عادةً بذلك.




وكيل ، ديكور ، محول ، وجسر هي كل الاختلافات على "التفاف" فئة. لكن استخداماتها مختلفة.

  • يمكن استخدام الوكيل عند الرغبة في إنشاء كائن كسلس أو إخفاء حقيقة أنك تتصل بخدمة بعيدة أو تتحكم في الوصول إلى الكائن.

  • يسمى الديكور أيضًا بـ "Smart Proxy". يتم استخدام هذا عندما تريد إضافة وظيفة إلى كائن ، ولكن ليس عن طريق توسيع نوع ذلك الكائن. هذا يسمح لك أن تفعل ذلك في وقت التشغيل.

  • يتم استخدام المحول عندما يكون لديك واجهة مجردة ، وترغب في تعيين هذه الواجهة إلى كائن آخر له دور وظيفي مماثل ، ولكن واجهة مختلفة.

  • الجسر مشابه جدًا للمهايئ ، لكننا نسميه Bridge عند تعريف كل من الواجهة المجردة والتنفيذ الأساسي. أي أنك لا تتكيف مع بعض التعليمة البرمجية القديمة أو الخارجية ، فأنت مصمم جميع التعليمات البرمجية ولكنك تحتاج إلى أن تتمكن من تبديل التطبيقات المختلفة.

  • الواجهة واجهة مستوى أعلى (قراءة: أبسط) إلى نظام فرعي من فئة واحدة أو أكثر. افترض أن لديك مفهومًا معقدًا يتطلب أن تمثِّل كائنات متعددة. إن إجراء تغييرات على مجموعة الكائنات هذه أمر مربك ، لأنك لا تعرف دائمًا أي كائن لديه الطريقة التي تحتاج إلى الاتصال بها. هذا هو الوقت المناسب لكتابة الواجهة التي توفر أساليب عالية المستوى لجميع العمليات المعقدة التي يمكنك القيام بها لجمع الأشياء. مثال: نموذج المجال لقسم المدرسة ، مع طرق مثل countStudents() ، reportAttendance() ، assignSubstituteTeacher() ، وما إلى ذلك.




هذا الاقتباس من Head First Design Patterns

التعاريف تنتمي إلى الكتاب. الأمثلة تنتمي لي.

Decorator - لا يغير الواجهة ، ولكنه يضيف المسؤولية. افترض أن لديك واجهة سيارة ، عند تنفيذ ذلك لنموذج مختلف من السيارة (s ، sv ، sl) قد تحتاج إلى إضافة المزيد من المسؤولية لبعض الموديلات. مثل قد فتحة السقف ، الوسادة الهوائية الخ ..

محول - تحويل واجهة واحدة إلى أخرى. لديك واجهة سيارة وتريد أن تتصرف مثل سيارة جيب. حتى تأخذ السيارة وتعديلها وتحويلها إلى سيارة جيب. لأنها ليست سيارة جيب حقيقية. لكن أفعال مثل سيارة جيب.

الواجهة - تجعل الواجهة أكثر بساطة. افترض أن لديك سيارة ، طائرة ، واجهات سفينة. في الواقع ، كل ما تحتاجه هو فصل يُرسل الأشخاص من موقع إلى آخر. تريد واجهة لتحديد ما لاستخدام المركبة. ثم تقوم بجمع كل هذه الإشارات المرجعية تحت مظلة واحدة واتركها تقرر / تفوضها لتبقيها بسيطة.

Head First: "إن الواجهة لا تبسط فقط واجهة ، بل تفصل العميل عن نظام فرعي من المكونات. قد تقوم الواجهات والمحوّلات بلف الطبقات المتعددة ، لكن هدف الواجهة هو التبسيط ، في حين أن المحول هو تحويل الواجهة إلى شيء مختلف. "




تتضمن جميع النماذج الأربعة التفاف الجسم / الطبقة الداخلية مع الطبقة الخارجية ، بحيث تكون متشابهة جدًا بنيوياً. أود أن أوجز الفرق حسب الغرض:

  • يقوم الوكيل بتضمين الوصول الخارجي إلى الداخلي.
  • ديكور يقوم بتعديل أو توسيع سلوك الداخلية مع الخارجي.
  • محول يحول واجهة من الداخل إلى الخارجي.
  • يفصل Bridge الجزء غير المتغير من السلوك (الخارجي) عن الجزء المتغير أو الأساسي الذي يعتمد على النظام الأساسي (داخلي).

ومن خلال تباين الواجهة بين الكائنات الداخلية والخارجية:

  • في واجهات وكيل هي نفسها.
  • في واجهات ديكور هي نفسها.
  • في واجهات محول مختلفة بشكل رسمي ، ولكن تحقيق نفس الغرض.
  • في واجهات بريدج مختلفة من الناحية المفاهيمية.



هناك قدر كبير من التداخل في العديد من أنماط GoF. انهم جميعا بنيت على قوة تعدد الأشكال وأحيانا تختلف فقط في الحقيقة في النية. (استراتيجية مقابل الدولة)

زاد فهمي للأنماط 100 أضعاف بعد قراءة أنماط التصميم الأولى للرأس .

انا اوصي بشده به!




وجهة نظري حول هذا الموضوع.

تحتوي جميع الأنماط الأربعة على الكثير من القواسم المشتركة ، وأحيانًا ما يطلق على الأربعة أنماطًا غير رسمية ، أو أنماط التفاف. كل تكوين الاستخدام ، وموضوع الالتفاف ، وتفويض التنفيذ للموضوع عند نقطة معينة ، تقوم بتعيين طريقة استدعاء واحدة إلى أخرى. إنهم يفقدون العميل ضرورة إنشاء كائن مختلف ونسخه على جميع البيانات ذات الصلة. إذا تم استخدامها بحكمة ، فإنها توفر الذاكرة والمعالج.

من خلال تعزيز اقتران فضفاض أنها تجعل مرة واحدة رمز ثابت أقل عرضة لتغيرات لا مفر منها ومقروءة أفضل لمطوري زميل.

مشترك كهربائي

يكيّف المحول الموضوع (adaptee) إلى واجهة مختلفة. بهذه الطريقة يمكننا إضافة كائن إلى مجموعة من أنواع مختلفة اسميًا.

يعرض المحول الأساليب ذات الصلة فقط للعميل ، ويمكنه تقييد الآخرين ، والكشف عن نوايا الاستخدام لسياقات معينة ، مثل تكييف المكتبة الخارجية ، وجعلها تبدو أقل عامة وأكثر تركيزًا على احتياجات تطبيقاتنا. تزيد المحولات من سهولة القراءة ووصف الذات لرمزنا.

يحمي محولات فريق واحد من التعليمات البرمجية المتغيرة من الفرق الأخرى؛ أداة لإنقاذ الحياة عند التعامل مع فرق في الخارج ؛-)

غرض أقل من ذلك لمنع الطبقة الموضوع من زيادة التعليقات التوضيحية. مع وجود العديد من الأطر القائمة على التعليقات التوضيحية ، يصبح هذا الاستخدام أكثر أهمية من أي وقت مضى.

محول يساعد على الحصول على ما يقرب من قيود جافا للوراثة واحدة فقط. يمكن أن يجمع بين عدة adaptees تحت ظرف واحد يعطي الانطباع من الميراث متعددة.

رمز الحكمة ، محول هو "رقيقة". لا ينبغي أن تضيف الكثير من التعليمات البرمجية إلى فئة التكيف ، بالإضافة إلى مجرد استدعاء طريقة التكيّف وتحويلات البيانات أحيانًا اللازمة لإجراء مثل هذه المكالمات.

لا توجد العديد من أمثلة المحولات الجيدة في JDK أو المكتبات الأساسية. مطوري التطبيقات إنشاء محولات ، لتكييف المكتبات إلى واجهات تطبيق محددة.

مزخرف

لا يقوم المُصمم فقط بتفويض ، وليس فقط بتعيين طريقة واحدة إلى أخرى ، بل يفعل المزيد ، ويقوم بتعديل سلوك بعض طرق الموضوع ، ويمكنه أن يقرر عدم استدعاء طريقة الموضوع على الإطلاق ، أو تفويضه إلى كائن مختلف ، أو كائن مساعد.

يقوم المصممون عادةً بإضافة وظائف (شفافة) إلى كائن ملفوف مثل التسجيل أو التشفير أو التنسيق أو الضغط على الهدف. هذه الوظيفة الجديدة قد تجلب الكثير من الكود الجديد. ومن ثم ، فالمصممين عادة ما يكونون "أكثر بدانة" ثم محولات.

يجب أن يكون الديكور فئة فرعية من واجهة الموضوع. يمكن استخدامها بشفافية بدلاً من موضوعاتها. انظر BufferedOutputStream ، فإنه لا يزال OutputStream ويمكن استخدامه على هذا النحو. هذا هو اختلاف فني كبير من محولات.

أمثلة كتابية لعائلة الديكور بأكملها في JDK - Java IO. جميع الطبقات مثل BufferedOutputStream و FilterOutputStream و ObjectOutputStream هي FilterOutputStream . يمكن أن تكون طبقات البصل ، حيث زينت واحدة من الديكور مرة أخرى ، إضافة المزيد من الوظائف.

الوكيل

الوكيل ليس مجمع نموذجي. قد لا يكون الكائن الملتف ، موضوع الوكيل ، موجودًا في وقت إنشاء الوكيل. الوكيل غالبا ما يخلق داخليا. قد يكون كائنًا ثقيلًا تم إنشاؤه عند الطلب ، أو أنه كائن بعيد في JVM مختلفة أو عقدة شبكة مختلفة وحتى كائن غير جافا ، وهو مكون في التعليمة البرمجية الأصلية. ليس من الضروري أن يلف أو يفوض إلى كائن آخر على الإطلاق.

تتمثل معظم الأمثلة النموذجية في الوكلاء البعيدين ، ومبدئي إنشاء الكائنات الثقيلة ووكلاء الوصول.

  • وكيل بعيد - الموضوع على الخادم البعيد ، JVM مختلفة أو حتى نظام غير جافا. يقوم الوكيل بترجمة استدعاءات الأسلوب لمكالمات RMI / REST / SOAP أو ما هو مطلوب ، مما يحمي العميل من التعرض للتقنية الأساسية.

  • Lazy Load Proxy - تهيئة الكائن بشكل كامل فقط للاستخدام الأول أو الاستخدام المكثف الأول.

  • وصول الوكيل - التحكم في الوصول إلى الموضوع.

مظهر زائف

ترتبط الواجهة ارتباطًا وثيقًا بتصميم مبدأ المعرفة الأقل (قانون ديميتر). واجهة تشبه إلى حد كبير محول. كلاهما يلتفان ، كلاهما يرسمان كائنًا لآخر ، لكنهما يختلفان في القصد. الواجهة تسطو الهيكل المعقد لموضوع ، الرسم البياني الكائن المعقد ، تبسط الوصول إلى بنية معقدة.

تلتف الواجهة على بنية معقدة ، مما يوفر واجهة مسطحة لها. هذا يمنع كائن العميل من التعرض للعلاقات الداخلية في بنية الموضوع وبالتالي تعزيز اقتران فضفاض.

جسر

متغير أكثر تعقيدًا من نمط المحول حيث لا يختلف التنفيذ فحسب بل أيضًا التجريد. يضيف أكثر غير مباشر إلى الوفد. الوفد الإضافي هو الجسر. ينسخ محول حتى من التكيف مع واجهة. إنه يزيد من التعقيد أكثر من أي أنماط التفاف أخرى ، لذا عليك أن تطبق بعناية.

الاختلافات في المنشئات

اختلافات نمط واضحة أيضا عند النظر في المنشئات.

  • الوكيل لا يلتف كائن موجود. لا يوجد موضوع في منشئ.

  • يقوم المصمم والمحول بتغليف الكائن الموجود بالفعل ، وعادةً ما يكون ذلك
    المقدمة في المنشئ.

  • يأخذ منشئ الواجهة عنصرًا أساسيًا للرسم البياني للكائنات بالكامل ، وإلا فإنها تبدو كالمحول.

مثال واقع الحياة - محول JAXB مارشالينغ . الغرض من هذا المحول هو تعيين فئة مسطحة بسيطة إلى هيكل أكثر تعقيدًا مطلوبًا خارجيًا ومنع طبقة الموضوع "الملوثة" مع التعليقات التوضيحية المفرطة.




تزيين الدوال مع عدد مختلف من الحجج:

def frame_tests(fn):
    def wrapper(*args):
        print "\nStart: %s" %(fn.__name__)
        fn(*args)
        print "End: %s\n" %(fn.__name__)
    return wrapper

@frame_tests
def test_fn1():
    print "This is only a test!"

@frame_tests
def test_fn2(s1):
    print "This is only a test! %s" %(s1)

@frame_tests
def test_fn3(s1, s2):
    print "This is only a test! %s %s" %(s1, s2)

if __name__ == "__main__":
    test_fn1()
    test_fn2('OK!')
    test_fn3('OK!', 'Just a test!')

نتيجة:

Start: test_fn1  
This is only a test!  
End: test_fn1  


Start: test_fn2  
This is only a test! OK!  
End: test_fn2  


Start: test_fn3  
This is only a test! OK! Just a test!  
End: test_fn3  




java design-patterns decorator bridge proxy-pattern