نموذج التصميم المحول
يفتقر محتوى هذه المقالة إلى الاستشهاد بمصادر. (مارس 2016) |
تحتوي هذه المقالة اصطلاحات معربة غير مُوثَّقة. لا تشمل ويكيبيديا العربية الأبحاث الأصيلة، ويلزم أن تُرفق كل معلومة فيها بمصدر موثوق به. (أكتوبر 2015) |
تحتاج هذه المقالة كاملةً أو أجزاءً منها لإعادة الكتابة حسبَ أسلوب ويكيبيديا. |
نموذج التصميم المحول هو نموذج عبارة عن محول، يقوم بعمل واجهة لنظام معقد، والتي قد تكون كود جاهز مثلاً أو إحدى المكتبات القديمة لديك وتريد أن تعيد استخدامها، ولكن الواجهة لهذا الكود أو المكتبة غير متوافق مع الكود الجديد، فنقوم بعمل محول يوافق بين الجزئيتين.
- ITarget: هو الواجهة المراد استخدامها من المستخدم النهائي للكود أو المستخدم للمحول.
- Adaptee:هو الكود أو المكتبة والمراد استخدامه.
- Adapter: هي الصنف التي تورث من ITarget وتوافق شروط الـ Adaptee
- Request: هو الطلب الذي نريد تنفيذه في النهاية.
- SpecificRequest: المحتوى الذي نريد تنفيذه والموجود في الـ Adaptee.
- نستعرض مثال بسيط لتوضيح الفكرة: في البداية نعرض الصنف Adaptee المُراد الإنتفاع من خدماته، وهو كود قديم أو ربما يكون كود حديث في نفس البرنامج ولكن نريد أن نستخدمه في مكان آخر لا يتوافق مع الواجهة الخاصة به ونريد أن نستبدله:
// Adapter Pattern - Simple Judith Bishop Oct 2007
// Simplest adapter using interfaces and inheritance
Existing way requests are implemented
class Adaptee
{
// Provide full precision
public double SpecificRequest(double a, double b)
{
return a / b;
}
}
هذه الواجهة الجديدة ITarget للكود الجديد الذي نريد أن نعمل عليه:
// Required standard for requests
interface ITarget
{
// Rough estimate required
string Request(int i);
}
هنا نقوم بتعريف الصنف Adapter تجمع بين القديم والحديث والتوفيق بينهم، حيث إن رغبنا في الصنف الجديد أن نمرر أي رقم فسيُّقسم تلقائيًا على 3 بدون أن نمرر الرقم 3، وهذا ما تقوم به الدالة Request حيث تقوم باستخدام الدالة القديمة SpecificRequest وتمرر الرقم الجديد مع تمرير الرقم 3، ونقوم أيضًا بتقريب الناتج إلى أقرب رقم صحيح.
// Implementing the required standard via Adaptee
class Adapter : Adaptee, ITarget
{
public string Request(int i)
{
return "Rough estimate is " + (int)Math.Round(SpecificRequest(i, 3));
}
}
الآن نقوم بعرض الصنف Client ونرى الفرق بين الحديث والقديم:
class Client
{
static void Main()
{
// Showing the Adapteee in standalone mode
Adaptee first = new Adaptee();
Console.Write("Before the new standard\nPrecise reading: ");
Console.WriteLine(first.SpecificRequest(5, 3));
// What the client really wants
ITarget second = new Adapter();
Console.WriteLine("\nMoving to the new standard");
Console.WriteLine(second.Request(5));
}
}
ليظهر الناتج كالتالي:
/* Output
42 Before the new standard
43 Precise reading: 1.66666666666667
44
45 Moving to the new standard
46 Rough estimate is 2
47 */
Two-Way Adapters
عدلقد يتم مواجهة مشكلة في حال القيام بعمليتين إحداهما تابعة لمكتبة معينة والأخرى تتبع لمكتبة ثانية، وتريد أن تجعلهما يعملان سويًا، فمثلًا لو أن مخترع حاول أن يصنع طائرة مائية لها هيكل طائرة ومحرك ولوحة تحكم لسفينة وأسماها Seabird، ويقوم بتشغيل الطائرة AirCraft والجزء المائي منها أثناء وجدودها على صفحة الماء SeaCradt وسوف يتحكم في اختراعه عن طريق Interface لكل جزء، وسوف يكون الجزء المتحكم في الإنتقال من الواجهة لآخرهو الـ Seabird. ولتحويل هذا المثال إلى كود برمجي يتم إتباع الخطوات التالية:
- في البداية نقوم بعمل الواجهة التي ستكون مسؤولة عن مرحلة الطيران:
// ITarget interface
public interface IAircraft
{
bool Airborne { get; }
void TakeOff();
int Height { get; }
}
كما نرى فهو يشمل على خاصية Airborne Property وأخرى Height والدالة Takeoff.
والآن نقوم بتوريث الواجهة لصف يكون مسؤول عن الطيران:
// Target
public sealed class Aircraft : IAircraft
{
int height;
bool airborne;
public Aircraft()
{
height = 0;
airborne = false;
}
public void TakeOff()
{
Console.WriteLine("Aircraft engine takeoff");
airborne = true;
height = 200; // Meters
}
public bool Airborne
{
get { return airborne; }
}
public int Height
{
get { return height; }
}
}
الصنف Aircraft كما نرى هو الصنف Sealed أي أن من غير الممكن أن نشتق منه صنفًا آخر، في هذه الصنف قمنا بملأ الواجهة - اولآن نقوم بعمل واجهة المتحكم في السفينة:
// Adaptee interface
public interface ISeacraft
{
int Speed { get; }
void IncreaseRevs();
}
كما نرى نحتاج فقط إلى الدالة IncreaseRevs وخاصية Speed - والآن نقوم بوراثة الواجهة:
// Adaptee implementation
public class Seacraft : ISeacraft
{
int speed = 0;
public virtual void IncreaseRevs()
{
speed += 10;
Console.WriteLine("Seacraft engine increases revs to " + speed + " knots");
}
public int Speed
{
get { return speed; }
}
}
نقوم الآن بعمل محول وبطبيعة الحال لا نستطيع الوراثة من صنفين فنرث من صنف وواجهة:
// Adapter
public class Seabird : Seacraft, IAircraft
{
int height = 0;
// A two-way adapter hides and routes the Target's methods
// Use Seacraft instructions to implement this one
public void TakeOff()
{
while (!Airborne)
IncreaseRevs();
}
// Routes this straight back to the Aircraft
public int Height
{
get { return height; }
}
// This method is common to both Target and Adaptee
public override void IncreaseRevs()
{
base.IncreaseRevs();
if (Speed > 40)
height += 100;
}
public bool Airborne
{
get { return height > 50; }
}
}
قمنا بالوراثة من صنف الـ Seacraft وواجهة الـ IAircraft وهذا بالطبع إلزامي وذلك لعدم امكانيتنا في السي شارب أن نرث من أكثر من صنف. والآن نحن قادرين على استخدام مميزات متعددة كمميزات السفينة والطائرة.
الآن نقوم بوضع المفتاح ونقوم بتشغيل السفينة نورماندي:
static void Main()
{
// No adapter
Console.WriteLine("Experiment 1: test the aircraft engine");
IAircraft aircraft = new Aircraft();
aircraft.TakeOff();
if (aircraft.Airborne) Console.WriteLine(
"The aircraft engine is fine, flying at "
+ aircraft.Height + "meters");
// Classic usage of an adapter
Console.WriteLine("\nExperiment 2: Use the engine in the Seabird");
IAircraft seabird = new Seabird();
seabird.TakeOff(); // And automatically increases speed
Console.WriteLine("The Seabird took off");
// Two-way adapter: using seacraft instructions on an IAircraft object
// (where they are not in the IAircraft interface)
Console.WriteLine("\nExperiment 3: Increase the speed of the Seabird:");
((ISeacraft)seabird).IncreaseRevs();
(seabird as ISeacraft).IncreaseRevs();
if (seabird.Airborne)
Console.WriteLine("Seabird flying at height " + seabird.Height +
" meters and speed " + (seabird as ISeacraft).Speed + " knots");
Console.WriteLine("Experiments successful; the Seabird flies!");
}
/* Output
Experiment 1: test the aircraft engine
Aircraft engine takeoff
The aircraft engine is fine, flying at 200 meters
Experiment 2: Use the engine in the Seabird
Seacraft engine increases revs to 10 knots
Seacraft engine increases revs to 20 knots
Seacraft engine increases revs to 30 knots
Seacraft engine increases revs to 40 knots
Seacraft engine increases revs to 50 knots
The Seabird took off
Experiment 3: Increase the speed of the Seabird:
Seacraft engine increases revs to 60 knots
Seacraft engine increases revs to 70 knots
Seabird flying at height 300 meters and speed 70 knots
Experiments successful; the Seabird flies!