في هندسة البرمجيات، نمط المحوّل (adapter pattern) هو نمط تصميم برمجيات (يُعرف أيضًا باسم المغلّف (wrapper)، تسمية بديلة مشتركة مع نمط الديكور) يسمح لواجهة صنف (class interface) موجود مسبقاً ليُستخدم كواجهة أخرى[1]. غالبًا ما يتم استخدامه لجعل الأصناف الموجودة تعمل مع الآخرين دون تعديل الكود المصدري (source code) الخاص بهم.
مثال على ذلك هو المحوّل (adapter )الذي يحوّل واجهة نموذج كائن لمستند (Document Object Model) من مستند XML إلى بنية شجرة يمكن عرضها.
نظرة عامة
يعد نمط تصميم المحوّل[2] أحد أنماط تصميم GoF الثلاثة والعشرون المعروفة التي تصف كيفية حل مشاكل التصميم المتكررة لتصميم برنامج مرن وقابل لإعادة الاستخدام، أي ذو توجيه كائني الذي يسهل تنفيذه (implement)، تغييره، اختباره وإعادة استخدامه.
يحل نمط تصميم المحوّل مشاكل مثل:[3]
- كيف يمكن إعادة استخدام صنف (class) لا يحتوي على واجهة (Interface) يطلبها العميل؟
- كيف يمكن للأصناف التي تحتوي على واجهات غير متوافقة (incompatible) أن تعمل معًا؟
- كيف يمكن تُوفّر واجهة بديلة لصنف؟
غالبًا لا يمكن إعادة استخدام صنف (موجود بالفعل) فقط لأن واجهته لا تتوافق مع الواجهة التي يطلبها العملاء (clients require).
يصف نمط تصميم المحوّل كيفية حل مثل هذه المشاكل:
- عرّف صنف محوّل منفصل (
adapter
) يحول الواجهة (غير المتوافقة(incompatible)) للصنف المحَوَل (adaptee
) إلى واجهة أخرى تُعتبر الهدف (target
) والتي يطلبها العملاء. - اعمل من خلال محوّل (
adapter
) للعمل مع (إعادة استخدام) أصناف ليس لها الواجهة المطلوبة.
الفكرة الرئيسية في هذا النمط هي العمل من خلال المحوّل المنفصل (adapter
)، الذي يقوم بتكييف (تحويل (adapts)) واجهة صنف (موجودة مسبقاً) دون تغييرها.
لا يعرف العملاء إذا ما كانوا يعملون مع الصنف الهدف مباشرة (target
) أم من خلال محوّل (adapter
) مع صنف لا يحتوي على الواجهة الهدف (target
).
انظر أيضًا الرسم التخطيطي للصنف UML أدناه.
تعريف
يسمح المحوّل لواجهتين غير متوافقتين (incompatible interfaces) بالعمل معًا. هذا هو التعريف الحقيقي للمحوّل. قد تكون الواجهات غير متوافقة، ولكن الوظيفة الداخلية(inner functionality) يجب أن تلبي الحاجة. يسمح نمط تصميم المحوّل للأصناف غير المتوافقة بالعمل معًا من خلال تحويل واجهة إحدى الأصناف (interface of one class) إلى واجهة يتوقعها العملاء (clients).
الاستخدام
يمكن استخدام المحوّل عندما يتوجب على المُفلّف (wrapper) الالتزام بواجهة معينة (particular interface) ويجب أن يدعم سلوك متعدد الأشكال (polymorphic behavior). بدلاً من ذلك، منفذ نمط الديكور يمكّن من إضافة أو تغيير سلوك واجهة (behavior of an interface) في وقت التشغيل (run-time)، ويتم استخدام واجهة البناء (facade) عند الرغبة في واجهة أسهل أو أبسط لكائن أساسي (underlying object). [4]
نمط | نوايا |
---|---|
محوّل أو مُغلّف (Adapter or wrapper) | يحول واجهة إلى أخرى بحيث تتطابق مع ما يتوقعه العميل. |
منفذ الديكور (Decorator) | يضيف المسؤولية إلى الواجهة ديناميكيًا من خلال تغليف (wrapping) الكود الأصلي. |
تفويض (Delegation) | دعم "التركيب على الميراث" ("composition over inheritance"). |
واجهة البناء (Facade) | يوفر واجهة مبسطة (simplified interface). |
هيكل
مخطط الصنف UML
في مخطط الصنف UML أعلاه، لا يمكن لصنف العميل (client
) الذي يتطلب الواجهة الهدف (target
) إعادة استخدام صنف المحَوَل (adaptee
) مباشرة لأن المحوَل (adaptee
) لا يتوافق مع الواجهة الهدف (target
) . بدلاً من ذلك، يقوم العميل (client
) خلال صنف المحوّل (adapter
) بتنفيذ الواجهة الهدف (target
) من حيث المحوَل (adaptee
) :
- أسلوب مُحوّل الكائن (
object adapter
) ينفذ الواجهة الهدف (target
) عن طريق التفويض إلى كائن محَوَل (adaptee
) في وقت التشغيل (run-time) (()adaptee.specificOperation
) (الكود:محَوَل.عملية محددة). - أسلوب مُحوّل الصنف (
class adapter
) ينفذ الواجهة الهدف (target
) من خلال الوراثة من صنف المحَوَل (adaptee
) في وقت التجميع (compile-time) (()specificOperation
) (الكود:عملية محددة).
نمط محوّل الكائن (Object adapter pattern)
في نمط المحوّل هذا، يحتوي المحوّل على مثيل من الصنف (an instance of the class) الذي يغلّفه (it wraps). في هذه الحالة، يقوم المحوّل بإجراء استدعاءات إلى مثيل الكائن المُغلّف (the instance of the wrapped object).
نمط محوّل الصنف (Class adapter pattern)
يستخدم نمط المحوّل هذا عدة واجهات متعددة الأشكال (multiple polymorphic interfaces) تقوم بتنفيذ (implementing) أو توريث كل من الواجهة المتوقعة والواجهة الموجودة مسبقًا (pre-existing). من المعتاد أن تكون الواجهة المتوقعة مُنشئة كصنف واجهة خالصة (pure interface class)، خاصة في لغات مثل جافا (Java) (قبل JDK 1.8) التي لا تدعم توارث الأصناف المتعدد (multiple inheritance of classes). [1]
شكل آخر من أنماط محوّل وقت التشغيل (runtime adapter)
الدافع من الحل وقت التجميع (compile time solution)
المرغوب به هو أن صنف ClassA
يجب أن يدعم صنف classB
مع بعض البيانات، لنفترض وجود بعض البيانات من سلاسل النص String
. الحل وقت التجميع (compile time) هو التالي:
classB.setStringData(classA.getStringData());
ومع ذلك، افترض أنه يجب تغيير تنسيق بيانات سلاسل النص String
. الحل وقت التجميع (compile time solution) هو استخدام الوراثة:
public class Format1ClassA extends ClassA { @Override public String getStringData() { return format(toString()); } }
وربما إنشاء كائن "التنسيق" ("formatting" object) بشكل صحيح في وقت التشغيل ( at runtime) عن طريق نمط المصنع (factory pattern).
حل محوّل وقت التشغيل (Run-time adapter solution)
باستخدام "المحوّلات" نحصل على الحل كما يلي:
1. عرّف واجهة "مزود" ("provider") وسيط، واكتبْ تنفيذ لواجهة المزود تلك (implementation of that provider interface) التي تغلف مصدر البيانات (wraps the source of the data)، ClassA
في هذا المثال، وتخرج (outputs) البيانات المنسقة (the data formatted) حسب الضرورة:
public interface StringProvider { public String getStringData(); } public class ClassAFormat1 implements StringProvider { private ClassA classA = null; public ClassAFormat1(final ClassA a) { classA = a; } public String getStringData() { return format(classA.getStringData()); } private String format(final String sourceValue) { // Manipulate the source string into a format required // by the object needing the source object's data return sourceValue.trim(); } }
2. اكتبْ صنف محوّل (adapter class) الذي يرجع (returns) التنفيذ المحدد للمزود (the specific implementation of the provider):
public class ClassAFormat1Adapter extends Adapter { public Object adapt(final Object anObject) { return new ClassAFormat1((ClassA) anObject); } }
3. سجّلْ المحوّل adapter
في سجل عالمي (global registry)، بحيث يمكن البحث عن المحوّل adapter
في وقت التشغيل (runtime):
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. يمكن رؤية الميزة في ذلك، إذا كان من المطلوب نقل البيانات بتنسيق (format) ثاني، فابحثْ عن المحوّل/ المزود المختلف:
Adapter adapter = AdapterFactory.getInstance() .getAdapterFromTo(ClassA.class, StringProvider.class, "format2");
6. وإذا كان من المطلوب إخراج (output) البيانات من صنف ClassA
، على سبيل المثال؛ بيانات لصورة في ClassC
:
Adapter adapter = AdapterFactory.getInstance() .getAdapterFromTo(ClassA.class, ImageProvider.class, "format2"); ImageProvider provider = (ImageProvider) adapter.adapt(classA); classC.setImage(provider.getImage());
7. بهذه الطريقة، استخدام المحوّلات والمزودين يسمح بتعدد "الرؤية" ( multiple "views") من قبل صنفا ClassB
و ClassC
في صنف ClassA
دون الحاجة إلى تغيير التسلسل الهرمي للأصناف (class hierarchy). بشكل عام، ما سبق يسمح بآلية لتدفق البيانات الغير محددة (arbitrary data flows) بين الكائنات التي يمكن تعديلها إلى التسلسل الهرمي للكائن الموجود (to an existing object hierarchy).
تنفيذ نمط المحوّل
عند تنفيذ نمط المحوّل (Implementation of the adapter pattern)، للتوضيح، يمكن للمرء تطبيق اسم الصنف من صنف إلى واجهة محوّل كالتالي [ClassName]To[Interface]Adapter
للتنفيذ الخاص بالمزود (to the provider implementation)، على سبيل المثال DAOToProviderAdapter
. يجب أن يكون لديه طريقة المنشئ (constructor method) مع متغير خاص بصنف المحَوَل (adaptee class variable) كمعلمة(parameter) . سيتم تمرير هذه المعلمة إلى مثيل عضو instance member of
من (اسم-الصنف)إلى(اسم-الواجهة) للمحوّل كالتالي [ClassName]To[Interface]Adapter
. عندما يتم استدعاء طريقة العميل (ClientMethod)، سيتمكن من الوصول إلى مثيل المحوَل (the adaptee instance) الذي يسمح بالوصول إلى البيانات المطلوبة للمحوَل (adaptee) وتنفيذ العمليات على تلك البيانات التي تولد المخرجات (output) المطلوبة.
كريستال Crystal
abstract class FormatIphone getter connector abstract def recharge abstract def use_lightning end abstract class FormatAndroid getter connector abstract def recharge abstract def use_micro_usb end class Iphone < FormatIphone def initialize @connector = false end def use_lightning @connector = true puts "Lightning connected" end def recharge if @connector puts "Recharge started" puts "Recharge finished" else puts "Connect Lightning first" Close end end class Android < FormatAndroid def initialize @connector = false end def use_micro_usb @connector = true puts "MicroUsb connected" end def recharge if @connector puts "Recharge started" puts "Recharge finished" else puts "Connect MicroUsb first" end end end class IphoneAdapter < FormatAndroid private getter mobile : FormatIphone def initialize(@mobile) end def recharge @mobile.recharge end def use_micro_usb puts "MicroUsb connected" @mobile.use_lightning end end class AndroidRecharger def initialize phone = Android.new phone.use_micro_usb phone.recharge end end class IphoneMicroUsbRecharger def initialize phone = Iphone.new phone_adapter = IphoneAdapter.new(phone) phone_adapter.use_micro_usb phone_adapter.recharge end end class IphoneRecharger def initialize phone = Iphone.new phone.use_lightning phone.recharge end end puts "Recharging android with MicroUsb Recharger" AndroidRecharger.new puts puts "Recharging iPhone with MicroUsb using Adapter pattern" IphoneMicroUsbRecharger.new puts puts "Recharging iPhone with iPhone Recharger" IphoneRecharger.new
المخرجات Output
Recharging android with MicroUsb Recharger MicroUsb connected Recharge started Recharge finished Recharging iPhone with MicroUsb using Adapter pattern MicroUsb connected Lightning connected Recharge started Recharge finished Recharging iPhone with iPhone Recharger Lightning connected Recharge started Recharge finished
جافا
interface LightningPhone { void recharge(); void useLightning(); } interface MicroUsbPhone { void recharge(); void useMicroUsb(); } class Iphone implements LightningPhone { private boolean connector; @Override public void useLightning() { connector = true; System.out.println("Lightning connected"); } @Override public void recharge() { if (connector) { System.out.println("Recharge started"); System.out.println("Recharge finished"); } else { System.out.println("Connect Lightning first"); } } } class Android implements MicroUsbPhone { private boolean connector; @Override public void useMicroUsb() { connector = true; System.out.println("MicroUsb connected"); } @Override public void recharge() { if (connector) { System.out.println("Recharge started"); System.out.println("Recharge finished"); } else { System.out.println("Connect MicroUsb first"); } } } /* exposing the target interface while wrapping source object */ class LightningToMicroUsbAdapter implements MicroUsbPhone { private final LightningPhone lightningPhone; public LightningToMicroUsbAdapter (LightningPhone lightningPhone) { this.lightningPhone = lightningPhone; } @Override public void useMicroUsb() { System.out.println("MicroUsb connected"); lightningPhone.useLightning(); } @Override public void recharge() { lightningPhone.recharge(); } } public class AdapterDemo { static void rechargeMicroUsbPhone(MicroUsbPhone phone) { phone.useMicroUsb(); phone.recharge(); } static void rechargeLightningPhone(LightningPhone phone) { phone.useLightning(); phone.recharge(); } public static void main(String[] args) { Android android = new Android(); Iphone iPhone = new Iphone(); System.out.println("Recharging android with MicroUsb"); rechargeMicroUsbPhone(android); System.out.println("Recharging iPhone with Lightning"); rechargeLightningPhone(iPhone); System.out.println("Recharging iPhone with MicroUsb"); rechargeMicroUsbPhone(new LightningToMicroUsbAdapter (iPhone)); } }
Output
Recharging android with MicroUsb MicroUsb connected Recharge started Recharge finished Recharging iPhone with Lightning Lightning connected Recharge started Recharge finished Recharging iPhone with MicroUsb MicroUsb connected Lightning connected Recharge started Recharge finished
دلفي Delphi
type ILightningPhone = interface ['{52628045-CF6C-41F0-ACCA-A65DCEE13BDC}'] procedure Recharge; procedure UseLightning; end; type IMicroUSBPhone = interface ['{436746B6-D02D-49E6-A5AC-F6D745DFD182}'] procedure Recharge; procedure UseMicroUSB; end; type TIPhone = class(TInterfacedObject, ILightningPhone) strict private FConnector: Boolean; public procedure Recharge; procedure UseLightning; end; type TAndroid = class(TInterfacedObject, IMicroUSBPhone) strict private FConnector: Boolean; public procedure Recharge; procedure UseMicroUSB; end; type TLightningToMicroUsbAdapter = class(TInterfacedObject, IMicroUSBPhone) strict private FLightningPhone: ILightningPhone; public constructor Create(const ALightningPhone: ILightningPhone); reintroduce; procedure Recharge; procedure UseMicroUSB; end; procedure RechargeLightningPhone(const ALightningPhone: ILightningPhone); begin ALightningPhone.UseLightning; ALightningPhone.Recharge; end; procedure RechargeMicroUSBPhone(const AMicroUSBPhone: IMicroUSBPhone); begin AMicroUSBPhone.UseMicroUSB; AMicroUSBPhone.Recharge; end; procedure TEdijsForm.PatternExampleButtonClick(Sender: TObject); var _Android: IMicroUSBPhone; _IPhone: ILightningPhone; _LightningToMicroUsbAdapter: IMicroUSBPhone; begin _Android := TAndroid.Create; WriteLn('Recharging android with MicroUsb'); RechargeMicroUSBPhone(_Android); _IPhone := TIPhone.Create; WriteLn('Recharging iPhone with Lightning'); RechargeLightningPhone(_IPhone); WriteLn('Recharging iPhone with MicroUsb'); _LightningToMicroUsbAdapter := TLightningToMicroUsbAdapter.Create(_IPhone); RechargeMicroUSBPhone(_LightningToMicroUsbAdapter); end;
المخرجات Output
Recharging android with MicroUsb MicroUsb connected Recharge started Recharge finished Recharging iPhone with Lightning Lightning connected Recharge started Recharge finished Recharging iPhone with MicroUsb MicroUsb connected Lightning connected Recharge started Recharge finished
بي أتش بي PHP
// Adapter Pattern example interface IFormatIPhone { public function recharge(); public function useLightning(); } interface IFormatAndroid { public function recharge(); public function useMicroUsb(); } // Adaptee class IPhone implements IFormatIPhone { private $connectorOk = FALSE; public function useLightning() { $this->connectorOk = TRUE; echo "Lightning connected -$\n"; } public function recharge() { if ($this->connectorOk) { echo "Recharge Started\n"; echo "Recharge 20%\n"; echo "Recharge 50%\n"; echo "Recharge 70%\n"; echo "Recharge Finished\n"; } else { echo "Connect Lightning first\n"; } } } // Adapter class IPhoneAdapter implements IFormatAndroid { private $mobile; public function __construct(IFormatIPhone $mobile) { $this->mobile = $mobile; } public function recharge() { $this->mobile->recharge(); } public function useMicroUsb() { echo "MicroUsb connected -> "; $this->mobile->useLightning(); } } class Android implements IFormatAndroid { private $connectorOk = FALSE; public function useMicroUsb() { $this->connectorOk = TRUE; echo "MicroUsb connected ->\n"; } public function recharge() { if ($this->connectorOk) { echo "Recharge Started\n"; echo "Recharge 20%\n"; echo "Recharge 50%\n"; echo "Recharge 70%\n"; echo "Recharge Finished\n"; } else { echo "Connect MicroUsb first\n"; } } } // client class MicroUsbRecharger { private $phone; private $phoneAdapter; public function __construct() { echo "---Recharging iPhone with Generic Recharger---\n"; $this->phone = new IPhone(); $this->phoneAdapter = new IPhoneAdapter($this->phone); $this->phoneAdapter->useMicroUsb(); $this->phoneAdapter->recharge(); echo "---iPhone Ready for use---\n\n"; } } $microUsbRecharger = new MicroUsbRecharger(); class IPhoneRecharger { private $phone; public function __construct() { echo "---Recharging iPhone with iPhone Recharger---\n"; $this->phone = new IPhone(); $this->phone->useLightning(); $this->phone->recharge(); echo "---iPhone Ready for use---\n\n"; } } $iPhoneRecharger = new IPhoneRecharger(); class AndroidRecharger { private $phone; public function __construct() { echo "---Recharging Android Phone with Generic Recharger---\n"; $this->phone = new Android(); $this->phone->useMicroUsb(); $this->phone->recharge(); echo "---Phone Ready for use---\n\n"; } } $androidRecharger = new AndroidRecharger(); // Result: #quanton81 //---Recharging iPhone with Generic Recharger--- //MicroUsb connected -> Lightning connected -$ //Recharge Started //Recharge 20% //Recharge 50% //Recharge 70% //Recharge Finished //---iPhone Ready for use--- // //---Recharging iPhone with iPhone Recharger--- //Lightning connected -$ //Recharge Started //Recharge 20% //Recharge 50% //Recharge 70% //Recharge Finished //---iPhone Ready for use--- // //---Recharging Android Phone with Generic Recharger--- //MicroUsb connected -> //Recharge Started //Recharge 20% //Recharge 50% //Recharge 70% //Recharge Finished //---Phone Ready for use---
سكالا Scala
implicit def adaptee2Adapter(adaptee: Adaptee): Adapter = { new Adapter { override def clientMethod: Unit = { // call Adaptee's method(s) to implement Client's clientMethod */ } } }
بايثون Python
""" Adapter pattern example. """ from abc import ABCMeta, abstractmethod NOT_IMPLEMENTED = "You should implement this." RECHARGE = ["Recharge started.", "Recharge finished."] POWER_ADAPTERS = {"Android": "MicroUSB", "iPhone": "Lightning"} CONNECTED = "{} connected." CONNECT_FIRST = "Connect {} first." class RechargeTemplate: __metaclass__ = ABCMeta @abstractmethod def recharge(self): raise NotImplementedError(NOT_IMPLEMENTED) class FormatIPhone(RechargeTemplate): @abstractmethod def use_lightning(self): raise NotImplementedError(NOT_IMPLEMENTED) class FormatAndroid(RechargeTemplate): @abstractmethod def use_micro_usb(self): raise NotImplementedError(NOT_IMPLEMENTED) class IPhone(FormatIPhone): __name__ = "iPhone" def __init__(self): self.connector = False def use_lightning(self): self.connector = True print(CONNECTED.format(POWER_ADAPTERS[self.__name__])) def recharge(self): if self.connector: for state in RECHARGE: print(state) else: print(CONNECT_FIRST.format(POWER_ADAPTERS[self.__name__])) class Android(FormatAndroid): __name__ = "Android" def __init__(self): self.connector = False def use_micro_usb(self): self.connector = True print(CONNECTED.format(POWER_ADAPTERS[self.__name__])) def recharge(self): if self.connector: for state in RECHARGE: print(state) else: print(CONNECT_FIRST.format(POWER_ADAPTERS[self.__name__])) class IPhoneAdapter(FormatAndroid): def __init__(self, mobile): self.mobile = mobile def recharge(self): self.mobile.recharge() def use_micro_usb(self): print(CONNECTED.format(POWER_ADAPTERS["Android"])) self.mobile.use_lightning() class AndroidRecharger(object): def __init__(self): self.phone = Android() self.phone.use_micro_usb() self.phone.recharge() class IPhoneMicroUSBRecharger(object): def __init__(self): self.phone = IPhone() self.phone_adapter = IPhoneAdapter(self.phone) self.phone_adapter.use_micro_usb() self.phone_adapter.recharge() class IPhoneRecharger(object): def __init__(self): self.phone = IPhone() self.phone.use_lightning() self.phone.recharge() print("Recharging Android with MicroUSB recharger.") AndroidRecharger() print() print("Recharging iPhone with MicroUSB using adapter pattern.") IPhoneMicroUSBRecharger() print() print("Recharging iPhone with iPhone recharger.") IPhoneRecharger()
مقالات ذات صلة
- التفويض (Delegation)، ذو صلة قوية بنمط محوّل الكائن (object adapter pattern).
- مبدأ انعكاس التبعية (Dependency inversion principle)، والذي يمكن اعتباره تطبيقًا لنمط المحوّل، عندما تعرّف الاصناف عالية المستوى (high-level class) واجهتها(المحوّل) الخاصة بها (its own (adapter) interface) إلى النموذج المنخفض المستوى (low-level module) (التي ينفذها صنف المحوّلات)((implemented by an adaptee class)).
- الهندسة المعمارية للمنافذ والمحوّلات (Ports and adapters architecture)
- شيم(حوسبة) Shim
- دالة المغلّف (Wrapper function)
- مكتبة المغلّف (Wrapper library)
المراجع
- Freeman, Eric; Freeman, Elisabeth; Sierra, Kathy; Bates, Bert (2004). Head First Design Patterns. [[أوريلي ميديا|]]. صفحة 244. . OCLC 809772256. مؤرشف من الأصل في 28 أبريل 201830 أبريل 2013.
- Gamma, Erich; Helm, Richard; Johnson, Ralph; Vlissides, John (1994). Design Patterns: Elements of Reusable Object-Oriented Software. Addison Wesley. صفحات 139ff. . مؤرشف من في 29 مايو 2020.
- "The Adapter design pattern - Problem, Solution, and Applicability". w3sDesign.com. مؤرشف من الأصل في 29 مايو 202012 أغسطس 2017.
- Freeman, Eric; Freeman, Elisabeth; Sierra, Kathy; Bates, Bert (2004). Hendrickson, Mike; Loukides, Mike (المحررون). Head First Design Patterns. 1. [[أوريلي ميديا|]]. صفحات 243, 252, 258, 260. . مؤرشف من الأصل في 29 مايو 202002 يوليو 2012.
- "The Adapter design pattern - Structure and Collaboration". w3sDesign.com. مؤرشف من الأصل في 19 أغسطس 201812 أغسطس 2017.