نمط المحول

نمط المحول
في هندسة البرمجيات، نمط المحوّل هو نمط تصميم برمجيات (يُعرف أيضًا باسم المغلّف؛ تسمية بديلة مشتركة مع نمط الديكور) يسمح لواجهة صنف موجود مسبقاً ليُستخدم كواجهة أخرى. غالبًا ما يتم استخدامه لجعل الأصناف الموجودة تعمل مع الآخرين دون تعديل الكود المصدري الخاص بهم.
مثال على ذلك هو المحوّل الذي يحوّل واجهة نموذج كائن المستند من مستند XML إلى بنية شجرة يمكن عرضها.
نظرة عامة
يعد نمط تصميم المحوّل أحد أنماط تصميم GoF الثلاثة والعشرون المعروفة التي تصف كيفية حل مشاكل التصميم المتكررة لتصميم برنامج مرن وقابل لإعادة الاستخدام، أي ذو توجيه كائني الذي يسهل تنفيذه، تغييره، اختباره وإعادة استخدامه.
يحل نمط تصميم المحوّل مشاكل مثل:
كيف يمكن إعادة استخدام صنف لا يحتوي على واجهة يطلبها العميل؟
كيف يمكن للأصناف التي تحتوي على واجهات غير متوافقة أن تعمل معًا؟
كيف يمكن تُوفّر واجهة بديلة لصنف؟
غالبًا لا يمكن إعادة استخدام صنف (موجود بالفعل) فقط لأن واجهته لا تتوافق مع الواجهة التي يطلبها العملاء.
يصف نمط تصميم المحوّل كيفية حل مثل هذه المشاكل:
عرّف صنف محوّل منفصل يحول الواجهة (غير المتوافقة) للصنف المحَوَل إلى واجهة أخرى تُعتبر الهدف والتي يطلبها العملاء.
اعمل من خلال محوّل للعمل مع (إعادة استخدام) أصناف ليس لها الواجهة المطلوبة.
الفكرة الرئيسية في هذا النمط هي العمل من خلال المحوّل المنفصل ، الذي يقوم بتكييف (تحويل) واجهة صنف (موجودة مسبقاً) دون تغييرها.
لا يعرف العملاء إذا ما كانوا يعملون مع الصنف الهدف مباشرة أم من خلال محوّل مع صنف لا يحتوي على الواجهة الهدف .
انظر أيضًا الرسم التخطيطي للصنف بلفة النمذجة الموحدة أدناه.
تعريف
يسمح المحوّل لواجهتين غير متوافقتين بالعمل معًا. هذا هو التعريف الحقيقي للمحوّل. قد تكون الواجهات غير متوافقة، ولكن الوظيفة الداخلية يجب أن تلبي الحاجة. يسمح نمط تصميم المحوّل للأصناف غير المتوافقة بالعمل معًا من خلال تحويل واجهة إحدى الأصناف إلى واجهة يتوقعها العملاء.
استخدام
يمكن استخدام المحوّل عندما يتوجب على المُغلّف الالتزام بواجهة معينة ويجب أن يدعم سلوك متعدد الأشكال. بدلاً من ذلك، منفذ نمط الديكور يمكّن من إضافة أو تغيير سلوك واجهة في وقت التشغيل، ويتم استخدام واجهة البناء عند الرغبة في واجهة أسهل أو أبسط لكائن أساسي.
هيكل
مخطط الصنف بلغة النمذجة الموحدة
في مخطط الصنف بلغة النمذجة الموحدة أعلاه، لا يمكن لصنف العميل الذي يتطلب الواجهة الهدف إعادة استخدام صنف المحَوَل مباشرة لأن المحوَل لا يتوافق مع الواجهة الهدف . بدلاً من ذلك، يقوم العميل خلال صنف المحوّل بتنفيذ الواجهة الهدف من حيث المحوَل :
أسلوب مُحوّل الكائن ينفذ الواجهة الهدف عن طريق التفويض إلى كائن محَوَل في وقت التشغيل (الكود: محَوَل.عملية محددة).
أسلوب مُحوّل الصنف ينفذ الواجهة الهدف من خلال الوراثة من صنف المحَوَل في وقت التجميع (الكود: عملية محددة).
نمط محوّل الكائن
نمط محوّل الكائن في نمط المحوّل هذا، يحتوي المحوّل على مثيل من الصنف الذي يغلّفه. في هذه الحالة، يقوم المحوّل بإجراء استدعاءات إلى مثيل الكائن المُغلّف.
نمط محوّل الصنف
نمط محوّل الصنف يستخدم نمط المحوّل هذا عدة واجهات متعددة الأشكال تقوم بتنفيذ أو توريث كل من الواجهة المتوقعة والواجهة الموجودة مسبقًا. من المعتاد أن تكون الواجهة المتوقعة مُنشئة كصنف واجهة خالصة (صافية)، خاصة في لغات مثل جافا (قبل JDK 1.8) التي لا تدعم توارث الأصناف المتعدد.
شكل آخر من أنماط محوّل وقت التشغيل
الدافع من الحل وقت التجميع
يجب أن يدعم صنف صنف مع بعض البيانات، لنفترض وجود بعض البيانات من سلاسل النص. الحل وقت التجميع هو التالي:
classB.setStringData(classA.getStringData()); ومع ذلك، افترض أنه يجب تغيير تنسيق بيانات السلاسل النصية في . الحل وقت التجميع هو استخدام الوراثة:
public class Format1ClassA extends ClassA {
@Override
public String getStringData() {
return format(toString());
}
} وربما إنشاء كائن «التنسيق» بشكل صحيح في وقت التشغيل عن طريق نمط المصنع.
حل محوّل وقت التشغيل
باستخدام «المحوّلات» نحصل على الحل كما يلي:
1. عرّف واجهة «مزود» وسيط، واكتبْ تنفيذ لواجهة المزود تلك التي تغلف مصدر البيانات، في هذا المثال، وتخرج البيانات المنسقة حسب الضرورة:
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. اكتبْ صنف محوّل الذي يرجع التنفيذ المحدد للمزود:
public class ClassAFormat1Adapter extends Adapter {
public Object adapt(final Object anObject) {
return new ClassAFormat1((ClassA) anObject);
}
}
3. سجّلْ المحوّل في سجل عالمي، بحيث يمكن البحث عن المحوّل في وقت التشغيل:
AdapterFactory.getInstance().registerAdapter(ClassA.class, ClassAFormat1Adapter.class, "تنسيق1");
4. في الكود، عند الرغبة في نقل البيانات من صنف إلى صنف ، اكتبْ:
Adapter adapter =
AdapterFactory.getInstance()
.getAdapterFromTo(ClassA.class, StringProvider.class, "تنسيق1");
StringProvider provider = (StringProvider) adapter.adapt(classA);
String string = provider.getStringData();
classB.setStringData(string); أو بشكل أكثر إيجازًا: classB.setStringData(((StringProvider)
AdapterFactory.getInstance()
.getAdapterFromTo(ClassA.class, StringProvider.class, "تنسيق1")
.adapt(classA))
.getStringData());
5. يمكن ملاحظة الفائدة من ذلك، إذا كان من المطلوب نقل البيانات بتنسيق ثاني، فابحثْ عن المحوّل/ المزود المختلف:
Adapter adapter =
AdapterFactory.getInstance()
.getAdapterFromTo(ClassA.class, StringProvider.class, "تنسيق2");
6. وإذا كان من المطلوب إخراج البيانات من صنف ، على سبيل المثال؛ بيانات لصورة في :
Adapter adapter =
AdapterFactory.getInstance()
.getAdapterFromTo(ClassA.class, ImageProvider.class, "تنسيق2");
ImageProvider provider = (ImageProvider) adapter.adapt(classA);
classC.setImage(provider.getImage());
7. بهذه الطريقة، استخدام المحوّلات والمزودين يسمح بتعدد «الرؤية» من قبل صنفا و في صنف دون الحاجة إلى تغيير التسلسل الهرمي للأصناف. بشكل عام، ما سبق يسمح بآلية لتدفق البيانات الغير محددة بين الكائنات التي يمكن تعديلها إلى التسلسل الهرمي للكائن الموجود.
تنفيذ نمط المحوّل
عند تنفيذ نمط المحوّل، للتوضيح، يمكن للمرء تطبيق اسم الصنف من صنف إلى واجهة محوّل كالتالي للتنفيذ الخاص بالمزود، على سبيل المثال . يجب أن يكون لديه طريقة المنشئ مع متغير خاص بصنف المحَوَل كمعلمة. سيتم تمرير هذه المعلمة إلى مثيل عضو من (اسم-الصنف) إلى (اسم-الواجهة) للمحوّل كالتالي . عندما يتم استدعاء طريقة العميل، سيتمكن من الوصول إلى مثيل المحوَل الذي يسمح بالوصول إلى البيانات المطلوبة للمحوَل وتنفيذ العمليات على تلك البيانات التي تولد المخرجات المطلوبة.
كريستال
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 المخرجات:
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));
}
}مخرجات:
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
دلفي
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;
المخرجات:
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
بي أتش بي
// 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---
سكالا
implicit def adaptee2Adapter(adaptee: Adaptee): Adapter = {
new Adapter {
override def clientMethod: Unit = {
// call Adaptee's method(s) to implement Client's clientMethod */
}
}
}
بايثون
"""
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()
ملحق: مسرد المصطلحات الإنجليزية
انظر أيضًا
التفويض، ذو صلة قوية بنمط محوّل الكائن.
مبدأ انعكاس التبعية ، والذي يمكن اعتباره تطبيقًا لنمط المحوّل، عندما تعرّف الاصناف عالية المستوى واجهتها (المحوّل) الخاصة بها إلى النموذج المنخفض المستوى (التي ينفذها صنف المحوّلات).
المراجع
نماذج تصميم البرمجيات