التوجيه (البرمجة الكائنية التوجه)

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

وفد

كثيرًا ما يُخلط بين التوجيه والتفويض ؛ فهما مفهومان متكاملان من الناحية النظرية. في كلتا الحالتين، يوجد كائنان، ويستخدم الكائن الأول (المرسل، المُغلِّف) الكائن الثاني (المستقبل، المُغلَّف)، على سبيل المثال لاستدعاء دالة. ويكمن الاختلاف بينهما في ما selfيُشير إليه الكائن المستقبل (أي في بيئة تقييم الدالة في الكائن المستقبل): ففي التفويض يُشير إلى الكائن المُرسل، بينما في التوجيه يُشير إلى الكائن المستقبل. تجدر الإشارة إلى أن هذا selfيُستخدَم غالبًا ضمنيًا كجزء من الإرسال الديناميكي (تحديد الدالة: أي دالة يُشير إليها اسم الدالة).

يكمن الفرق بين إعادة التوجيه والتفويض في ربط المعامل "self" في الكائن المُستَقبَل عند استدعائه عبر المُغلِّف. في التفويض، يرتبط المعامل "self" بالمُغلِّف، بينما في إعادة التوجيه يرتبط بالكائن المُستَقبَل. ... إعادة التوجيه هي شكل من أشكال إعادة إرسال الرسائل تلقائيًا؛ أما التفويض فهو شكل من أشكال الوراثة مع ربط الأصل (الفئة العليا) في وقت التشغيل، بدلًا من وقت الترجمة/الربط كما هو الحال في الوراثة "العادية". [ 1 ]

على سبيل المثال، بالنظر إلى الكود التالي:

// دالة الإرسال void n () { print ( "n1" ); }// دالة الاستقبال void m () { print ( "m2, " ); n (); }void n () { print ( "n2" ); }

في حالة التفويض، m()سيتم الإخراجم2، ن1لأنها n()تُقيّم في سياق الكائن الأصلي (المرسل)، بينما في حالة إعادة التوجيه، ستُخرجم2، ن2لأنها n()تُقيّم في سياق الكائن المُستقبِل. [ 1 ]

في الاستخدام غير الرسمي، يُشار إلى إعادة التوجيه غالبًا باسم "التفويض"، أو يُعتبر شكلًا من أشكال التفويض، ولكن في الاستخدام الدقيق، يتم التمييز بينهما بوضوح من خلال ما selfيُشير إليه. فبينما يُشابه التفويض الوراثة ، إذ يسمح بإعادة استخدام السلوك (وإعادة استخدام الكود تحديدًا ) دون تغيير سياق التقييم، يُشابه إعادة التوجيه التركيب ، حيث يعتمد التنفيذ فقط على الكائن المُستقبِل (العضو)، وليس على الكائن المُرسِل (الأصلي). في كلتا الحالتين، تكون إعادة الاستخدام ديناميكية، أي يتم تحديدها في وقت التشغيل (بناءً على الكائن الذي يتم تفويض الاستخدام إليه أو إعادة توجيهه)، وليست ثابتة، أي يتم تحديدها في وقت الترجمة/الربط (بناءً على الفئة التي يتم الوراثة منها). ومثل الوراثة، يسمح التفويض للكائن المُرسِل بتعديل السلوك الأصلي، ولكنه عُرضة لمشاكل تُشابه مشاكل الفئة الأساسية الهشة ؛ بينما يُوفر إعادة التوجيه تغليفًا أقوى ويتجنب هذه المشاكل؛ انظر التركيب مقابل الوراثة . [ 1 ]

أمثلة

مثال بسيط على التوجيه الصريح في جافا: يقوم مثيل Bبتوجيه استدعاءات إلى fooطريقة حقله a:

class B { A a ; T foo () { return a . foo (); } }

لاحظ أنه عند تنفيذ الأمر a.foo()، thisيكون الكائن a(نوعًا فرعيًا من A)، وليس الكائن الأصلي (نسخة من B). علاوة على ذلك، aليس بالضرورة أن يكون نسخة من A، فقد يكون نسخة من نوع فرعي. في الواقع، Aليس بالضرورة أن يكون فئة: فقد يكون واجهة/ بروتوكولًا .

على النقيض من الوراثة، حيث fooيتم تعريفها في فئة أساسية A(والتي يجب أن تكون فئة، وليست واجهة)، وعند استدعائها على نسخة من فئة فرعية B، فإنها تستخدم الكود المحدد في A، ولكن thisالكائن لا يزال نسخة من B:

class A { T foo () { /* ... */ }; }class B extends A { }

في مثال بايثون هذا، تقوم الفئة Bبتمرير fooالطريقة والخاصية xإلى الكائن في حقلها a: استخدام هذه على b(مثيل من B) هو نفسه استخدامها على b.a(مثيل الذي Aيتم تمرير هذه إليه).

class A : def __init__ ( self , x ) -> None : self . x = xdef foo ( self ) : print ( self.x )class B : def __init__ ( self , a ) -> None : self . a = adef foo ( self ): self . a . foo ()@property def x ( self ): return self . a . x@ x.setter def x ( self , x ) : self.a.x = x@ x.deleter def x ( self ) : del self.a.xa = A ( 42 ) b = B ( a ) b.foo () # يطبع '42'. b.x # قيمته ' 42 ' b.x = 17 # قيمة bax الآن 17 del b.x # يحذف bax

بسيط

مخطط فئات UML الذي يوضح عملية إعادة التوجيه.
مخطط فئات UML الذي يوضح عملية إعادة التوجيه.

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

إعادة التوجيه هي ببساطة نقل مهمة ما إلى شخص أو جهة أخرى. إليك مثال بسيط:

class RealPrinter { // المُستقبِل void print () { System . out . println ( "Hello world!" ); } }class Printer { // المُرسِل RealPrinter p = new RealPrinter (); // إنشاء المُستقبِل void print () { p.print (); // استدعاء المُستقبِل } } public class Main { public static void main ( String [] arguments ) { // يبدو للعالم الخارجي أن Printer تطبع بالفعل. Printer printer = new Printer (); printer.print ( ) ; } }

معقد

الحالة الأكثر تعقيدًا هي نمط الديكور ، الذي يسمح باستخدام الواجهات لجعل عملية التوجيه أكثر مرونة وأمانًا من حيث النوع . تعني "المرونة" هنا عدم الحاجة إلى الرجوع إلى الواجهة بأي شكل من الأشكال، حيث يتم تجريد عملية تبديل التوجيه منها . في هذا المثال، يمكن للفئة توجيه البيانات إلى أي فئة تُنفذ واجهة معينة . تحتوي الفئة على دالة للتبديل إلى مُوجِّه آخر. يُحسِّن تضمين بنود `return` من أمان النوع ، لأن كل فئة مُلزمة بتنفيذ الدوال الموجودة في الواجهة. أما المقابل الرئيسي فهو زيادة حجم الكود.CABCCICimplements

interface I { void f ( ); void g (); } class A implements I { public void f () { System.out.println ( " A : doing f()" ) ; } public void g ( ) { System.out.println ( " A: doing g()" ) ; } } class B implements I { public void f ( ) { System.out.println ( " B : doing f()" ) ; } public void g ( ) { System.out.println ( " B : doing g()" ); } } // تغيير الكائن المُنفِّذ في وقت التشغيل (يتم عادةً في وقت الترجمة) class C implements I { I i = null ; // إعادة التوجيه public C ( I i ) { setI ( i ); } public void f () { i.f ( ) ; } public void g () { i.g ( ) ; } // السمات العادية public void setI ( I i ) { this.i = i ; } } public class Main { public static void main ( String [] arguments ) { C c = new C ( new A ()); c . f (); // الإخراج: A: تنفيذ f() c . g (); // الإخراج: A: تنفيذ g() c . setI ( new B ()); c . f(); // الناتج: B: تنفيذ f() c . g (); // الناتج: B: تنفيذ g() } }

التطبيقات

يُستخدم التوجيه في العديد من أنماط التصميم. [ 2 ] يُستخدم التوجيه مباشرةً في عدة أنماط:

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

مراجع

  1. 1 2 3 بوشي، مارتن؛ ويك، فولفغانغ (2000). "Generic Wrappers" (ملف PDF) . ECOOP 2000 - البرمجة كائنية التوجه . سلسلة محاضرات في علوم الحاسوب. المجلد  1850. الصفحات 212-213 . doi : 10.1007/3-540-45102-1_10 . ISBN  978-3-540-67660-7.
  2. جاما، إريك ؛ هيلم، ريتشارد ؛ جونسون، رالف ؛ فليسيدس، جون (1995). أنماط التصميم: عناصر البرمجيات القابلة لإعادة الاستخدام والموجهة للكائنات . أديسون-ويسلي . رمز Bibcode : 1995dper.book.....G . ISBN 978-0-201-63361-0.