نمط المحول

في هندسة البرمجيات ، يُعد نمط المُهايئ نمطًا لتصميم البرمجيات (يُعرف أيضًا باسم المُغلِّف ، وهو تسمية بديلة مشتركة مع نمط المُزخرف ) يسمح باستخدام واجهة فئة موجودة كواجهة أخرى. [ 1 ] : 244 ويُستخدم غالبًا لجعل الفئات الموجودة تعمل مع فئات أخرى دون تعديل شفرتها المصدرية .

ومن الأمثلة على ذلك محول يقوم بتحويل واجهة نموذج كائن المستند لمستند XML إلى بنية شجرية يمكن عرضها.

ملخص

يُعد نمط تصميم المحول [ 2 ] أحد أنماط تصميم عصابة الأربعة المعروفة البالغ عددها 23 نمطًا والتي تصف كيفية حل مشكلات التصميم المتكررة لتصميم برامج كائنية التوجه مرنة وقابلة لإعادة الاستخدام، أي كائنات يسهل تنفيذها وتغييرها واختبارها وإعادة استخدامها.

يحل نمط تصميم المحول مشاكل مثل: [ 3 ]

  • كيف يمكن إعادة استخدام فئة لا تحتوي على واجهة يحتاجها العميل؟
  • كيف يمكن للفئات التي لها واجهات غير متوافقة أن تعمل معًا؟
  • كيف يمكن توفير واجهة بديلة لفئة معينة؟

في كثير من الأحيان لا يمكن إعادة استخدام فئة (موجودة بالفعل) لمجرد أن واجهتها لا تتوافق مع الواجهة التي يتطلبها العملاء.

يصف نمط تصميم المحول كيفية حل مثل هذه المشكلات:

  • قم بتعريف فئة منفصلة adapterتقوم بتحويل واجهة (غير متوافقة) لفئة ( adaptee) إلى واجهة أخرى ( target) يحتاجها العملاء.
  • العمل من خلال آلية adapterللعمل مع (إعادة استخدام) الفئات التي لا تحتوي على الواجهة المطلوبة.

الفكرة الأساسية في هذا النمط هي العمل من خلال عنصر منفصل adapterيقوم بتكييف واجهة فئة (موجودة بالفعل) دون تغييرها.

لا يعرف العملاء ما إذا كانوا يعملون مع targetفئة بشكل مباشر أو من خلال adapterفئة لا تحتوي على targetالواجهة.

انظر أيضًا إلى مخطط فئات UML أدناه.

تعريف

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

الاستخدام

يمكن استخدام المحوّل عندما يجب أن يلتزم الغلاف بواجهة معينة وأن يدعم السلوك متعدد الأشكال . بدلاً من ذلك، يتيح نمط المُزخرف إضافة أو تغيير سلوك واجهة ما أثناء التشغيل، ويُستخدم نمط الواجهة عندما تكون هناك حاجة إلى واجهة أسهل أو أبسط لكائن أساسي. [ 1 ] : 243، 252، 258، 260

نمطنية
محول أو غلافيحوّل واجهة إلى أخرى بحيث تتطابق مع ما يتوقعه العميل.
مصمم ديكوريضيف بشكل ديناميكي مسؤولية إلى الواجهة عن طريق تغليف الكود الأصلي
وفددعم "التكوين على حساب الوراثة"
الواجهةيوفر واجهة مبسطة

بناء

مخطط فئات UML

مخطط فئة UML نموذجي لنمط تصميم المحول [ 4 ]

في مخطط فئات UML أعلاه ، لا يمكن للفئة التي تتطلب واجهة إعادة استخدام الفئة مباشرةً لأن واجهتها لا تتوافق مع تلك الواجهة. بدلاً من ذلك، تعمل الفئة من خلال فئة تُنفذ الواجهة وفقًا لما يلي :clienttargetadapteetargetclientadaptertargetadaptee

  • object adapterتُنفذ هذه الطريقة targetالواجهة عن طريق التفويض إلى adapteeكائن في وقت التشغيل ( adaptee.specificOperation()).
  • class adapterتُنفذ هذه الطريقة targetالواجهة عن طريق الوراثة من adapteeفئة في وقت الترجمة ( specificOperation()).

نمط محول الكائنات

في نمط المحول هذا، يحتوي المحول على نسخة من الفئة التي يغلفها. في هذه الحالة، يقوم المحول باستدعاءات إلى نسخة الكائن المغلف .

نمط محول الكائنات المعبر عنه في لغة النمذجة الموحدة (UML)
نمط محول الكائن المعبر عنه في LePUS3

نمط محول الفئة

يستخدم نمط المحول هذا واجهات متعددة الأشكال تُنفذ أو ترث كلاً من الواجهة المتوقعة والواجهة الموجودة مسبقًا. من الشائع إنشاء الواجهة المتوقعة كفئة واجهة خالصة ، خاصةً في لغات مثل جافا (قبل JDK 1.8) التي لا تدعم الوراثة المتعددة للفئات. [ 1 ] : 244

نمط محول الفئة المعبر عنه في لغة النمذجة الموحدة (UML)
نمط محول الفئة المعبر عنه في LePUS3

شكل آخر من أشكال نمط محول وقت التشغيل

الدافع من حل وقت الترجمة

المطلوب هو تزويد النظام ببعض البيانات، ولنفترض أنها بيانات معينة. الحل المُتاح أثناء الترجمة هو:classAclassBString

فئة ب . setStringData ( classA . getStringData ());

لكن، لنفترض أن تنسيق بيانات السلسلة النصية يجب أن يتغير. الحل الأمثل في وقت الترجمة هو استخدام الوراثة:

public class Format1ClassA extends ClassA { @Override public String getStringData () { return format ( toString ()); } }

وربما إنشاء كائن "التنسيق" الصحيح في وقت التشغيل عن طريق نمط المصنع .

حل محول وقت التشغيل

يتم تنفيذ الحل باستخدام "المحولات" على النحو التالي:

  1. قم بتعريف واجهة "موفر" وسيطة، واكتب تطبيقًا لتلك الواجهة التي تغلف مصدر البيانات، ClassAفي هذا المثال، وتخرج البيانات بتنسيق مناسب:
    واجهة StringProvider { public String getStringData (); }public class ClassAFormat1 Implements StringProvider { public ClassA classA = null ;public ClassAFormat1 ( Final ClassA a ) { classA = a ; }public String getStringData () { return format ( classA . getStringData ()); }private String format ( final String sourceValue ) { // تحويل سلسلة المصدر إلى تنسيق مطلوب // للكائن الذي يحتاج إلى بيانات كائن المصدر return sourceValue . trim (); } }
  2. اكتب فئة محول تُعيد التنفيذ المحدد للموفر:
    public class ClassAFormat1Adapter extends Adapter { public Object adapt ( final Object anObject ) { return new ClassAFormat1 (( ClassA ) anObject ); } }
  3. قم بتسجيلها adapterفي سجل عالمي، بحيث adapterيمكن البحث عنها في وقت التشغيل:
    AdapterFactory.getInstance ( ) . registerAdapter ( ClassA.class , ClassAFormat1Adapter.class , " format1 " ) ;
  4. في البرمجة، عند الرغبة في نقل البيانات من ClassAإلى ClassB، اكتب:
    Adapter adapter = AdapterFactory.getInstance ( ) . getAdapterFromTo ( ClassA.class , StringProvider.class , " format1 " ) ; StringProvider provider = ( StringProvider ) adapter.adapt ( classA ) ; String string = provider.getStringData ( ) ; classB.setStringData ( string ) ;

    أو باختصار أكثر:

    classB.setStringData ( ( ( StringProvider ) AdapterFactory.getInstance ( ) . getAdapterFromTo ( ClassA.class , StringProvider.class , " format1 " ) . adapt ( classA ) ) . getStringData ( ) ) ;
  5. تكمن الميزة في أنه إذا رغبنا في نقل البيانات بتنسيق ثانٍ، فيمكننا البحث عن المحول/المزود المختلف:
    Adapter adapter = AdapterFactory.getInstance ( ) . getAdapterFromTo ( ClassA.class , StringProvider.class , " format2 " ) ;
  6. وإذا رغبنا في إخراج البيانات من البرنامج ClassA، على سبيل المثال، كبيانات صورة في : ClassC
    Adapter adapter = AdapterFactory.getInstance ( ) . getAdapterFromTo ( ClassA.class , ImageProvider.class , " format2 " ) ; ImageProvider provider = ( ImageProvider ) adapter.adapt ( classA ) ; classC.setImage ( provider.getImage ( ) ) ;
  7. بهذه الطريقة، يتيح استخدام المحولات والموفرات إمكانية الوصول إلى "وجهات نظر" متعددة من ClassBوإلى ClassCالبيانات ClassAدون الحاجة إلى تغيير التسلسل الهرمي للفئات. وبشكل عام، يسمح ذلك بآلية لتدفق البيانات بين الكائنات، والتي يمكن تعديلها لتناسب التسلسل الهرمي الحالي للكائنات.

تطبيق نمط المحول

عند تطبيق نمط المُهايئ، ولتوضيح الفكرة، يُمكن استخدام اسم الفئة لتنفيذ المُزوّد؛ على سبيل المثال، `<اسم الفئة>`. يجب أن يحتوي على دالة إنشاء تأخذ مُتغير فئة المُهايئ كمعامل. سيتم تمرير هذا المعامل إلى عضو مثيل من `<اسم الفئة>`. عند استدعاء دالة العميل، ستتمكن من الوصول إلى مثيل المُهايئ، مما يسمح بالوصول إلى البيانات المطلوبة للمُهايئ وإجراء عمليات عليها لتوليد المُخرجات المطلوبة.[ClassName]To[Interface]AdapterDAOToProviderAdapter[ClassName]To[Interface]Adapter

جافا

package org.wikipedia.examples ;interface ILightningPhone { void recharge (); void useLightning (); }واجهة IMicroUsbPhone { void recharge (); void useMicroUsb (); }class Iphone implements ILightningPhone { private boolean connector ;@Override public void useLightning () { connector = true ; System . out . println ( "تم الاتصال بشبكة Lightning" ); }@Override public void recharge () { if ( connector ) { System.out.println ( " بدأ الشحن" ) ; System.out.println ( " اكتمل الشحن " ) ; } else { System.out.println ( " قم بتوصيل Lightning أولاً " ) ; } } }class Android implements IMicroUsbPhone { private boolean connector ;@Override public void useMicroUsb () { connector = true ; System . out . println ( "تم توصيل MicroUsb" ); }@Override public void recharge () { if ( connector ) { System.out.println ( " بدأ الشحن" ) ; System.out.println ( " اكتمل الشحن " ); } else { System.out.println ( " قم بتوصيل MicroUsb أولاً" ); } } } / * عرض واجهة الهدف مع تغليف كائن المصدر * / class LightningToMicroUsbAdapter implements IMicroUsbPhone { private final ILightningPhone lightningPhone ;public LightningToMicroUsbAdapter ( ILightningPhone lightningPhone ) { this . lightningPhone = lightningPhone ; }@Override public void useMicroUsb () { System . out . println ( "تم توصيل MicroUsb" ); lightningPhone . useLightning (); }@Override public void recharge () { lightningPhone . recharge (); } }public class AdapterDemo { static void rechargeMicroUsbPhone ( IMicroUsbPhone phone ) { phone . useMicroUsb (); phone . recharge (); }static void rechargeLightningPhone ( ILightningPhone phone ) { phone . useLightning (); phone . recharge (); }public static void main ( String [] args ) { Android android = new Android (); Iphone iPhone = new Iphone ();System.out.println ( " إعادة شحن هاتف أندرويد باستخدام منفذ MicroUsb" ); rechargeMicroUsbPhone ( android );System.out.println ( " إعادة شحن الآيفون باستخدام منفذ Lightning" ) ; rechargeLightningPhone ( iPhone );System.out.println ( " إعادة شحن الآيفون باستخدام منفذ MicroUsb" ) ; rechargeMicroUsbPhone ( new LightningToMicroUsbAdapter ( iPhone )); } }

الناتج

شحن أجهزة أندرويد باستخدام منفذ MicroUSB تم توصيل منفذ MicroUsb بدأت عملية إعادة الشحن اكتملت عملية الشحن إعادة شحن الآيفون باستخدام منفذ Lightning اتصال البرق بدأت عملية إعادة الشحن اكتملت عملية الشحن شحن الآيفون باستخدام منفذ MicroUSB تم توصيل منفذ MicroUsb اتصال البرق بدأت عملية إعادة الشحن اكتملت عملية الشحن 

بايثون

مثال على نمط المحول. من abc استورد ABCMeta و abstractmethod من typing استورد NoReturnإعادة الشحن : قائمة [ نص ] = [ "بدأ الشحن." , "انتهى الشحن." ] محولات الطاقة : قاموس [ نص ، نص ] = { "أندرويد" : "MicroUSB" ، "آيفون" : "Lightning" } رسالة الاتصال : نص = " تم الاتصال بـ {} ." رسالة الاتصال أولاً : نص = "قم بتوصيل {} أولاً."class RechargeTemplate ( metaclass = ABCMeta ): @abstractmethod def recharge ( self ) -> NoReturn : raise NotImplementedError ( "يجب عليك تنفيذ هذا." )class FormatIPhone ( RechargeTemplate ): @abstractmethod def use_lightning ( self ) -> NoReturn : raise NotImplementedError ( "يجب عليك تنفيذ هذا." )class FormatAndroid ( RechargeTemplate ): @abstractmethod def use_micro_usb ( self ) -> NoReturn : raise NotImplementedError ( "يجب عليك تنفيذ هذا." )class IPhone ( FormatIPhone ): __name__ : str = "iPhone"def __init __ ( self ) : self.connector : bool = Falseدالة استخدام Lightning ( self ) - > None : self.connector = True print ( CONNECTED_MSG.format ( POWER_ADAPTERS [ self .__ name__ ] ) )دالة إعادة الشحن ( self ) -> None : إذا كان self.connector : لكل حالة في RECHARGE : اطبع ( الحالة ) وإلا : اطبع ( CONNECT_FIRST_MSG.format ( POWER_ADAPTERS [ self .__ name__ ] ) )class Android ( FormatAndroid ): __name__ : str = "Android"def __init __ ( self ) - > None : self.connector : bool = Falseدالة استخدام منفذ USB صغير ( self ) - > لا شيء : self.connector = True print ( CONNECTED_MSG.format ( POWER_ADAPTERS [ self .__ name__ ] ) )دالة إعادة الشحن ( self ) -> None : إذا كان self.connector : لكل حالة في RECHARGE : اطبع ( الحالة ) وإلا : اطبع ( CONNECT_FIRST_MSG.format ( POWER_ADAPTERS [ self .__ name__ ] ) )class IPhoneAdapter ( FormatAndroid ): def __init __ ( self , mobile : FormatAndroid ) - > None : self.mobile : FormatAndroid = mobiledef recharge ( self ) -> None : self . mobile . recharge ()def use_micro_usb ( self ) - > None : print ( CONNECTED_MSG.format ( POWER_ADAPTERS [ " Android " ] ) ) self.mobile.use_lightning ( )class AndroidRecharger : def __init__ ( self ) -> None : self . phone : Android = Android () self . phone . use_micro_usb () self . phone . recharge ()class IPhoneMicroUSBRecharger : def __init__ ( self ) -> None : self . phone : IPhone = IPhone () self . phone_adapter : IPhoneAdapter = IPhoneAdapter ( self . phone ) self . phone_adapter . use_micro_usb () self . phone_adapter . recharge ()class IPhoneRecharger : def __init__ ( self ) -> None : self . phone : IPhone = IPhone () self . phone . use_lightning () self . phone . recharge ()print ( "إعادة شحن جهاز أندرويد باستخدام شاحن MicroUSB." ) AndroidRecharger () print ()print ( "إعادة شحن الآيفون باستخدام منفذ MicroUSB بنمط المحول." ) IPhoneMicroUSBRecharger () print ()print ( "إعادة شحن الآيفون باستخدام شاحن الآيفون." ) IPhoneRecharger ()

سي شارب

namespace Wikipedia.Examples ;باستخدام النظام ؛واجهة ILightningPhone { void ConnectLightning (); void Recharge (); }واجهة IUsbPhone { void ConnectUsb (); void Recharge (); }فئة مغلقة AndroidPhone : IUsbPhone { متغير منطقي خاص isConnected ;public void ConnectUsb () { this . isConnected = true ; Console . WriteLine ( "تم توصيل هاتف Android." ); }public void Recharge () { if ( this . isConnected ) { Console . WriteLine ( "جاري شحن هاتف أندرويد." ); } else { Console . WriteLine ( "قم بتوصيل كابل USB أولاً." ); } } }فئة مغلقة ApplePhone : ILightningPhone { متغير منطقي خاص isConnected ;public void ConnectLightning () { this . isConnected = true ; Console . WriteLine ( "تم توصيل هاتف Apple." ); }public void Recharge () { if ( this . isConnected ) { Console . WriteLine ( "يتم شحن هاتف Apple." ); } else { Console . WriteLine ( "قم بتوصيل كابل Lightning أولاً." ); } } }فئة مغلقة LightningToUsbAdapter : IUsbPhone { خاص للقراءة فقط ILightningPhone lightningPhone ;متغير منطقي خاص isConnected ؛public LightningToUsbAdapter ( ILightningPhone lightningPhone ) { this . lightningPhone = lightningPhone ; }public void ConnectUsb () { this . lightningPhone . ConnectLightning (); }public void Recharge () { this . lightningPhone . Recharge (); } }public class AdapterDemo { static void Main ( string [] args ) { ILightningPhone applePhone = new ApplePhone (); IUsbPhone adapterCable = new LightningToUsbAdapter ( applePhone ); adapterCable . ConnectUsb (); adapterCable . Recharge (); } }

الناتج:

هاتف آبل متصل. هاتف آبل قيد الشحن.

انظر أيضاً

مراجع

  1. 1 2 3 فريمان، إريك؛ فريمان، إليزابيث؛ سييرا، كاثي ؛ بيتس، بيرت (2004). أنماط التصميم من Head First . دار نشر أورايلي ميديا . ص  244. ISBN 978-0-596-00712-6OCLC 809772256. مؤرشف من النسخة الأصلية ( غلاف ورقي) بتاريخ 4 مايو 2013. تم الاطلاع عليه بتاريخ 30 أبريل 2013 . 
  2. غاما، إريك؛ هيلم، ريتشارد؛ جونسون، رالف؛ فليسيدس، جون (1994). أنماط التصميم: عناصر البرمجيات القابلة لإعادة الاستخدام والموجهة للكائنات . أديسون ويسلي . ص 139 وما بعدها . ISBN  0-201-63361-2.
  3. "نمط تصميم المحوّل - المشكلة والحل والتطبيق" . w3sDesign.com . مؤرشف من الأصل بتاريخ 28 أغسطس 2017. تم الاطلاع عليه بتاريخ 12 أغسطس 2017 .
  4. "نمط تصميم المحوّل - البنية والتعاون" . w3sDesign.com . مؤرشف من الأصل بتاريخ 28-08-2017 . تم الاطلاع عليه بتاريخ 12-08-2017 .