الرئيسيةعريقبحث

حقن التبعية


☰ جدول المحتويات


في هندسة البرمجيات ، يعد حقن التبعية (dependency injection)‏ تقنية يتلقى فيها كائن كائنات أخرى يعتمد عليها . تسمى هذه الكائنات الأخرى التبعيات.

في العلاقة النموذجية "باستخدام" [1] يسمى الكائن المتلقي عميل (client)‏ ويسمى الكائن الذي تم تمريره (أي تم حقنه ) خدمة (service)‏ . يمكن أن يكون الكود الذي ينقل الخدمة إلى العميل أنواعًا كثيرة ويسمى الحاقن . بدلاً من تحديد العميل للخدمة التي سيستخدمها، يخبر الحاقن العميل بالخدمة التي سيستخدمها. تشير "الحقن" إلى تمرير التبعية (خدمة) إلى الكائن (العميل) الذي قد يستخدمها.

الخدمة تصبح جزءاً من حالة العميل (client's state)‏. [2] يعد تمرير الخدمة إلى العميل، بدلاً من السماح للعميل ببناء الخدمة أو العثور عليها ( find the service)‏ ، شرطاً أساسياً للنمط .

النية من حقن التبعية هو تحقيق فصل الاهتمامات ( separation of concerns)‏ الخاصة بالبناء واستخدام الكائنات . يمكن أن يؤدي ذلك إلى زيادة إمكانية القراءة وإعادة استخدام الكود.

حقن التبعية هو شكل من أشكال التقنية الأوسع لعكس التحكم (broader technique of inversion of control)‏. لا يجب على العميل الذي يريد استدعاء بعض الخدمات معرفة كيفية إنشاء هذه الخدمات . بدلاً من ذلك، يفوض العميل مسؤولية توفير خدماته للكود الخارجي (الحاقن). لا يُسمح للعميل باستدعاء كود الحاقن[3] ؛ الحاقن هو الذي يبني الخدمات. ثم يقوم الحاقن بحقن (تمرير) الخدمات في العميل التي قد تكون موجودة بالفعل أو قد يتم بناؤها بواسطة الحاقن. ثم يستخدم العميل الخدمات . هذا يعني أن العميل لا يحتاج إلى معرفة الحاقن ، وكيفية إنشاء الخدمات، أو حتى الخدمات الفعلية التي يستخدمها. يحتاج العميل فقط إلى معرفة الواجهات الجوهرية للخدمات لأن هذه تحدد كيفية استخدام العميل للخدمات . وهذا يفصل مسؤولية "الاستخدام" ("use")‏ عن مسؤولية "البناء" ("construction")‏.

نوايا (Intent)‏

حقن التبعية (Dependency Injection)‏ يحل مشاكل مثل:[4]

  • كيف يمكن أن يكون التطبيق (application)‏ أو الصنف (class)‏ مستقل عن كيفية إنشاء كائناته (be independent of how its objects are created)‏ ؟
  • كيف يمكن تحديد طريقة إنشاء الكائنات (way objects are created)‏ في ملفات تكوين منفصلة (reated be specified in separate configuration files)‏ ؟
  • كيف يمكن لتطبيق (application )‏ دعم (support )‏ تكوينات (configurations)‏ مختلفة؟

إنشاء كائنات مباشرة داخل الصنف (Creating objects directly within the class)‏ أمر غير مرن لأنه يلزم الصنف بكائنات معينة ويجعل من المستحيل تغيير التمثيل (instantiation)‏ الفوري بشكل مستقل عن الصنف(دون الحاجة إلى تغيير) . يوقف الصنف عن إمكانية إعادة استخدامه (It stops the class from being reusable)‏

إذا كانت هناك حاجة إلى كائنات أخرى، (it makes the class hard to test)‏ ويجعل من الصعب اختبار الصنف (class)‏ لأنه لا يمكن استبدال الكائنات الحقيقية بكائنات وهمية ( because real objects can not be replaced with mock objects)‏.

لم يعد الصنف (class)‏ مسؤول عن إنشاء الكائنات التي يتطلبها، ولا يتعين عليه تفويض (delegate)‏ إنشاء مثيل (instantiation)‏ لكائن مصنع (to a factory object)‏ كما هو الحال في نمط تصميم (Abstract Factory)‏ [5] .
انظر أيضًا صنف UML ومخطط التسلسل أدناه.

نظرة عامة

Dependency injection for five-year-olds

When you go and get things out of the refrigerator for yourself, you can cause problems. You might leave the door open, you might get something Mommy or Daddy doesn't want you to have. You might even be looking for something we don't even have or which has expired.

What you should be doing is stating a need, "I need something to drink with lunch," and then we will make sure you have something when you sit down to eat.
John Munsch, 28 October 2009.[6][7][8]

حقن التبعية (Dependency injection)‏ يفصل إنشاء تبعيات العميل (creation of a client's dependencies)‏ عن سلوك العميل (client's behavior)‏، والذي يسمح لتصاميم البرنامج أن تكون مقترنة بشكل متساهل [9] (program designs to be loosely coupled)‏ ومتابعة انعكاس التبعية(to follow the dependency inversion )‏ و مبادئ المسؤولية واحدة (single responsibility principles)‏ [10]. يتناقض بشكل مباشر مع نمط محدد مواقع الخدمة

(It directly contrasts with the service locator pattern)‏، والذي يسمح للعملاء (clients)‏ بمعرفة النظام الذي يستخدمونه للعثور على التبعيات.(to know about the system they use to find dependencies)‏ .الحقن (injection)‏، الوحدة الأساسية لحقن التبعية (the basic unit of dependency injection)‏ ، ليست آلية جديدة أو مخصصة . يعمل بنفس الطريقة التي يعمل بها " تمرير المعلمة "("parameter passing")‏ .[11]

بالإشارة إلى "تمرير المعلمة" حيث أن الحقن يحمل carries ضمناً إضافياًthe added implication أنه يتم القيام به لعزل العميل عن التفاصيل to isolate the client from details ..

إن الحقن (injection )‏ يتعلق أيضًا بما يتحكم في المرور (control of the passing)‏ (وليس العميل) (never the client) مطلقًا وهو مستقل عن كيفية تحقيق التمرير(is independent of how the passing is accomplished)‏ , ، سواء عن طريق تمرير مرجع أو قيمة(by passing a reference or a value )‏ يشمل حقن التبعية أربعة أدوار:

  • كائن(ات) خدمة service لاستخدامها.
  • كائن العميل(client)‏ الذي يعتمد (depending )‏على الخدمة (الخدمات) (service(s))‏ التي يستخدمها.
  • الواجهات (interfaces)‏ التي تحدد كيفية استخدام العميل (client )‏ للخدمات (services)‏
  • الحاقن ( injector)‏، وهو المسؤول (responsible )‏عن إنشاء الخدمات (constructing the services)‏ وحقنها في العميل (injecting them into the client)‏

كمثال(As an analogy)‏:

  • خدمة(service )‏ - سيارة كهربائية أو غاز أو هجينة(hybrid )‏ أو ديزل.
  • العميل(client)‏ - السائق (driver )‏الذي يستخدم السيارة بنفس الطريقة بغض النظر عن المحرك(engine)‏
  • واجهة (interface )‏ - تلقائية (automatic)‏، تضمن (ensures)‏ ألا يضطر السائق إلى فهم تفاصيل المحرك مثل التروس (gears)‏
  • حاقن(injector)‏ - الوالد(parent )‏ الذي اشترى السيارة للطفل وقرر أي نوع(decided which kind)‏.

أي كائن(object )‏يمكن استخدامه يمكن اعتباره خدمة(service)‏ . يمكن اعتبار أي كائن يستخدم كائنات أخرى عميلاً(client)‏ . لا علاقة للأسماء بماهية الكائنات(what the objects are for)‏ وكل شيء يتعلق بالدور الذي تلعبه الكائنات (the role the objects play)‏ في أي حقن (in any one injection)‏.

الواجهات interfaces هي الأنواع (types)‏ التي يتوقع العميل (client )‏ تبعياتها (expects its dependencies to be)‏ .. المشكلة (issue )‏هي ما يجعلها متاحة (accessible)‏. قد تكون بالفعل أنواع من الواجهة يتم تنفيذها بواسطة الخدمات(may truly be interface types implemented by the services )‏ ولكن قد تكون أيضًا أصناف مجردة (Abstract classes)‏ أو حتى الخدمات نفسها (may be abstract classes or even the concrete services themselves)‏ على الرغم من أن هذا الأخير قد ينتهك DIP (though this last would violate DIP )‏[12] ويضحي بفك الارتباط الديناميكي الذي يتيح الاختبار(sacrifice the dynamic decoupling that enables testing)‏. يتطلب (required)‏ فقط ألا يعرف العميل هويته ( does not know which they are )‏ وبالتالي لا يتعامل معه على أنه ملموس، على سبيل المثال من خلال إنشائها أو توسيعها..

يجب ألا يكون لدى العميل (client)‏ معرفة ملموسة بالتنفيذ المحدد لتبعياته (no concrete knowledge of the specific implementation of its dependencies)‏ . يجب أن يعرف فقط اسم الواجهة (interface's name)‏ وواجهة برمجة التطبيقات (API)‏. ونتيجة لذلك، لن يحتاج العميل إلى التغيير حتى إذا تغير ما وراء الواجهة (even if what is behind the interface changes)‏ . ومع ذلك، إذا تمت إعادة هيكلة الواجهة (nterface is refactored )‏ من صنف إلى نوع واجهة (أو العكس) (from being a class to an interface type (or vice versa))‏ فسيحتاج العميل إلى إعادة التجميع ( recompiled.[13] هذا مهمsignificant )‏ ()‏ إذا تم نشر العميل والخدمات بشكل منفصل (if the client and services are published separately)‏ . . هذا الاقتران (coupling)‏ المؤسف هو واحد لا يمكن حله بالتبعية (is one that dependency injection cannot resolve)‏..يقدم الحاقن' (injector)‏ الخدمات (services)‏ إلى العميل (client)‏. في كثير من الأحيان، يقوم أيضًا بإنشاء العميل (constructs the client)‏. قد يربط الحاقن معًا رسمًا بيانيًا معقدًا جدًا للكائن عن طريق معاملة كائن مثل العميل وبعد ذلك كخدمة لعميل آخر( An injector may connect together a very complex object graph by treating an object like a client and later as a service for another client)‏.. قد يكون الحاقن (injector)‏. في الواقع العديد من الكائنات تعمل معًا (many objects working together)‏ ولكن قد لا يكون العميل( but may not be the client)‏. قد تتم الإشارة ( referred)‏إلى الحاقن (injector)‏ بأسماء أخرى مثل: المجمع، الموفر، الحاوية، المصنع، البناء، الربيع، كود البناء، أو الرئيسي( by other names such as: assembler, provider, container, factory, builder, spring, construction code, or main.)‏.

يمكن تطبيق حقن التبعية ( Dependency injection)‏ كنظام ( discipline)‏ ، واحد يطلب من جميع الكائنات صنف(class )‏ البناء والسلوك (one that asks that all objects separate construction and behavior )‏. يمكن أن يؤدي الاعتماد على إطار عمل حقن التبعية (DI framework )‏ بالقيام بالإنشاء ( to perform construction)‏ إلى حظر استخدام الكلمة المفتاحية "new" ( can lead to forbidding the use of the new keyword)‏ ، أو بشكل أقل صرامة ( less strictly)‏، السماح بالبناء المباشر لعناصر القيمة فقط ( only allowing direct construction of value objects)‏ .[14][15][16][17]

التصنيف

يعد عكس التحكم (Inversion of control)‏ (IoC) أكثر عمومية (more general)‏ من حقن التبعية. ببساطة، يعني انعكاس التحكم (IoC )‏ السماح لكود آخر بالاتصال بك بدلاً من الإصرار على إجراء الاستدعاء (Put simply, IoC means letting other code call you rather than insisting on doing the calling)‏ . مثال على عكس التحكم (IoC )‏ بدون حقن التبعية هو نمط طريقة القالب (template method pattern)‏ . هنا، يتم تحقيق تعدد الأشكال(polymorphism)‏ من خلال التصنيف الفرعي (subclassing)‏,، أي الميراث (inheritance)‏.[18] يطبق حقن التبعية انعكاس التحكم (IoC )‏ من خلال ( composition )‏ التكوين، لذلك غالبًا ما يكون مطابقًا لنمط الإستراتيجية ( strategy pattern)‏، ولكن في حين أن نمط الإستراتيجية مخصص للتبعيات لتكون قابلة للتبادل طوال عمر الكائن (but while the strategy pattern is intended for dependencies to be interchangeable throughout an object's lifetime)‏ , في حقن التبعية، قد يتم استخدام مثيل واحد فقط من التبعية (it may be that only a single instance of a dependency is used.)‏ [19] هذا لا يزال يحقق تعدد الأشكال، ولكن من خلال التفويض والتكوين (.This still achieves polymorphism, but through delegation and composition)‏.

أطر حقن التبعية (Dependency injection frameworks)‏

أطر التطبيقات (Application frameworks)‏ مثل CDI وتنفيذها (implementation)‏ مثل Weld و Spring و Guice و Play framework و Salta و Glassfish HK2 و Dagger وإطار التوسعة المدار Managed Extensibility Framework(MEF) تدعم حقن التبعية ولكن غير الزامية للقيام بحقن التبعيةdependency injection .[20][20][21]

مزايا

()‏

  • يسمح حقن التبعية للعميل بمرونة كونه قابلاً للتكوين (configurable)‏. فقط سلوك العميل يتم إصلاحه(Only the client's behavior is fixed)‏ . قد يقوم العميل بأي شيء يدعم الواجهة الجوهرية التي يتوقعها العميل. (the intrinsic interface the client expects)‏ .[22]
  • يمكن استخدام حقن التبعية لتجسيد تكوين النظام(externalize a system's configuration details)‏ في ملفات التكوين(configuration files)‏، مما يسمح بإعادة تكوين(reconfigured )‏النظام دون إعادة التجميع(recompilation)‏. يمكن كتابة تكوينات(configurations )‏ منفصلة التي تتطلب تنفيذات مختلفة للمكوّن(components)‏. وهذا يشمل، على سبيل المثال لا الحصر، الاختبار(testing)‏.[23]
  • نظرًا لأن حقن التبعية لا يتطلب أي تغيير في سلوك الكود(code behavior)‏، يمكن تطبيقه على الكود القديم باعتباره إعادة هيكلة الكود (refactoring)‏ .. والنتيجة هي عملاء أكثر استقلالية وأكثر سهولة في اختبار الوحدة في العزل باستخدام(البذرة )‏ stubs كائنات وهمية( mock objects)‏ التي تحاكي كائنات أخرى ليست تحت الاختبار. غالبًا ما تكون سهولة الاختبار هذه أول فائدة ملحوظة عند استخدام حقن التبعية.[24]
  • يسمح حقن التبعية للعميل بإزالة جميع ما هو معروف بالنسبة للتطبيق المحدد(all knowledge of a concrete implementation)‏ الذي يحتاج إلى استخدامه. هذا يساعد على عزل العميل من تأثير تغييرات التصميم والعيوب(impact of design changes and defects)‏ . يعزز قابلية إعادة الاستخدام(reusability )‏ والاختبار (testability )‏وقابلية الصيانة.(maintainability)‏ [25]
  • الحد من كود المتداول (boilerplate code)‏ في كائنات التطبيق، حيث أن الكل يعمل لتهيئة(initialize)‏أو إعداد التبعيات(set up dependencies)‏ يتم معالجته بواسطة مكون المزود (provider component)‏.
  • يسمح حقن التبعية بالتطوير(development )‏المتزامن(concurrent )‏ أو المستقل(independent )‏. يمكن لمطورين اثنين بشكل مستقل تطوير أصناف تستخدم بعضها البعض، كل ما تحتاجه هو فقط معرفة الواجهة(interface )‏التي ستتواصل من خلالها الاصناف(classes )‏ . غالبًا ما يتم تطوير البرنامج المساعدة(Plugins )‏ من خلال متاجر الجهات الخارجية (third party shops )‏ التي لا تتحدث أبدًا مع المطورين الذين أنشؤوا المنتج الذي يستخدم البرنامج المساعد.[26]
  • يقلل حقن التبعية الاقتران(coupling )‏ بين صنف(class )‏ و التبعية .(dependency)‏ [27][28]

سلبيات

  • يعمل حقن التبعية على إنشاء عملاء(clients)‏ يطلبون توفير تفاصيل التكوين(configuration details)‏ عن طريق(be supplied by)‏ كود البناء(construction code)‏.. يمكن أن يكون هذا مرهقًا(onerous)‏ عندما تتوفر افتراضيات واضحة (obvious defaults are available)‏.[26]
  • يمكن أن يجعل حقن التبعية من الصعب تتبع الكود (قراءتها) (code difficult to trace (read) )‏ لأنها تفصل بين السلوك والبناء. هذا يعني أنه يجب على المطورين الرجوع إلى المزيد من الملفات لمتابعة كيفية أداء النظام (system performs)‏.[26]
  • يتم تنفيذ(implemented )‏أطر حقن التبعية(Dependency injection frameworks)‏ مع انعكاس أو برمجة ديناميكية(reflection or dynamic programming)‏. يمكن أن يعيق هذا استخدام أتمتة IDE (IDE automation)‏ ، مثل "العثور على مراجع"( "find references")‏ و "إظهار التسلسل الهرمي للاستدعاءات ""(show call hierarchy)‏ وإعادة البناء الآمنة (safe refactorings.)‏ [29]
  • عادة ما يتطلب حقن التبعية المزيد من جهود التطوير المسبقupfront development effort حيث لا يمكن للمرء أن يستدعي أن يكون شيئًا صحيحًا في الوقت والمكان المطلوبين ولكن يجب أن يطلب حقنه ثم التأكد من أنه تم حقنه(since one can not summon into being something right when and where it)‏. is needed but must ask that it be injected and then ensure that it has been injected.[30]
  • يجبر حقن التبعية التعقيد على الانتقال من الأصناف (Dependency injection forces complexity to move out of classes)‏إلى الروابط بين الأصناف(into the linkages between classes)‏ التي قد لا تكون دائمًا مرغوبة أو سهلة الإدارة.d(be desirable or easily manage)‏.[31]
  • يمكن لحقن التبعية أن يشجع على الاعتماد على إطار حقن التبعية.( dependence on a dependency injection framework)‏.[32][33]

هيكل (بناء) (Structure)‏

مخطط الصنف UML ومخطط التتابع


نموذج لصنف UML ومخطط تسلسلي لنمط تصميم حقن التبعية.(A sample UML class and sequence diagram for the Dependency Injection design pattern)‏ [34].[34]

فيUML (UML)‏ الرسم التخطيطي لصنف (class diagram)‏أعلاه، لا يقوم صنف Client الذي يتطلب كائنات ServiceA و ServiceB بإنشاء صنفيServiceA1 و ServiceB1 مباشرة. بدلاً من ذلك، يقوم صنف Injector بإنشاء الكائنات وحقنها في Client ، مما يجعل Client مستقلًا عن كيفية إنشاء الكائنات (أي الأصناف الملموسة ( concrete classes )‏ التي يتم إنشاء مثيل لها). (which concrete classes are instantiated))‏ يوضح (UML)‏ (sequence diagram)‏ الرسم التخطيطي لتسلسل UML تفاعلات وقت التشغيل (interactions run-time)‏ يقوم كائن Injector بإنشاء كائنات ServiceA1 و ServiceB1 . بعد ذلك، يقوم Injector بإنشاء كائن Client وإدخال كائنات ServiceA1 و ServiceB1 .

أمثلة

بدون حقن التبعية

في مثال Java التالي، يحتوي صنف(Client )‏ على خدمة متغير عضو (member variable)‏ تمت تهيئته(initialized )‏بواسطة مُنشئ(Client)‏

(by the constructor Client)‏.

يتحكم(controls )‏ العميل(client )‏في تنفيذ الخدمة المستخدمة(which implementation of service is used)‏ ويتحكم في بنائه(controls its construction)‏ا. . في هذه الحالة، يُقال أن العميل لديه تبعية ذات كود صلب(hard-coded dependency)‏ على . (ExampleService)‏

// An example without dependency injection public class Client { // Internal reference to the service used by this client private ExampleService service; // Constructor Client() { // Specify a specific implementation in the constructor instead of using dependency injection service = new ExampleService(); } // Method within this client that uses the services public String greet() { return "Hello " + service.getName(); } }

يعتبر حقن(Dependency injection )‏التبعية تقنية بديلة(alternative technique)‏ لتهيئة متغير العضو(to initialize the member variable )‏ بدلاً من إنشاء كائن خدمة بشكل صريح(explicitly creating a service object)‏ كما هو موضح أعلاه. يمكننا تعديل هذا المثال

باستخدام التقنيات المختلفة الموضحة والموضحة في الأقسام الفرعية أدناه.(using the various techniques described and illustrated in the subsections below)‏ .

أنواع حقن التبعية

هناك ثلاث طرق على الأقل يمكن لكائن العميل(client object)‏ تلقي مرجع لوحدة خارجية(There are at least three ways a client object can receive a reference to an external module)‏::[35]

حقن المنشئ(constructor injection)‏
يتم توفير التبعيات من خلال مُنشئ صنف العميل.(The dependencies are provided through a client's class constructor)‏.
حقن المعيّن (setter injection)‏
يكشف العميل عن طريقة الضبط التي يستخدمها الحاقن لحقن التبعية.(The client exposes a setter method that the injector uses to inject the dependency)‏.
حقن الواجهة(interface injection)‏
توفر واجهة التبعية طريقة حاقن تضخ التبعية في أي عميل يتم تمريره إليها. يجب على العملاء تنفيذ واجهة تكشف عن طريقة تعيين يقبل التبعية.(The dependency's interface provides an injector method that will inject the dependency into any client passed to it. Clients must implement an interface that exposes a setter method that accepts the dependency.)‏

أنواع أخرى

من الممكن أن يكون لأطر حقن التبعية( DI frameworks)‏ أنواع أخرى من الحقن بخلاف تلك المذكورة أعلاه.[36] ()‏ قد تستخدم أطر الاختبار(Testing frameworks)‏ أيضًا أنواعًا أخرى. بعض أطر الاختبار الحديثة (modern testing frameworks)‏ لا تتطلب حتى أن يقبل العملاء بنشاط حقن التبعية وبالتالي جعل الكود القديم قابل للاختبار. على وجه الخصوص، في لغة جافا، من الممكن استخدام الانعكاس(reflection )‏ لجعل السمات الخاصة(private attributes)‏ عامة(public )‏ عند الاختبار وبالتالي قبول الحقن عن طريق التعيين(by assignment)‏ .[37]

لا تقدم بعض محاولات عكس التحكم الإزالة الكاملة للتبعية(full removal of dependency)‏، ولكن بدلاً من ذلك ببساطة استبدال أحد أشكال التبعية بأخرى. كقاعدة عامة، إذا لم يتمكن المبرمج من النظر إلى شيء سوى كود العميل (client code )‏و الإخبار عن إطار العمل المستخدم، عندئذٍ يكون لدى العميل اعتماد مرتبط بالكود الصلب (hard-coded dependency )‏على الإطار(framework)‏.

حقن المنشئ (Constructor injection)‏

تتطلب هذه الطريقة(method)‏ من العميل توفير معلمة(parameter )‏في مُنشئ (constructor)‏ للتبعية (for the dependency)‏.

// Constructor Client(Service service) { // Save the reference to the passed-in service inside this client this.service = service; }

حقن معيّن (Setter injection)‏

تتطلب هذه الطريقة من العميل توفير طريقة ضبط(setter method )‏ للتبعية.

// Setter method public void setService(Service service) { // Save the reference to the passed-in service inside this client. this.service = service; }

حقن الواجهة (Interface injection)‏

ببساطة العميل هو الذي ينشر واجهة الدور لطرق تعيين تبعيات العميل(role interface to the setter methods of the client's dependencies)‏. يمكن استخدامه لتحديد الطريقة التي يجب أن يتحدث بها الحاقن مع العميل عند حقن التبعيات.

// Service setter interface. public interface ServiceSetter { public void setService(Service service); } // Client class public class Client implements ServiceSetter { // Internal reference to the service used by this client. private Service service; // Set the service that this client is to use. @Override public void setService(Service service) { this.service = service; } }

مقارنة حقن المنشئ(Constructor injection comparison)‏

يُفضل عند إنشاء جميع التبعيات أولاً لأنه يمكن استخدامها لضمان أن يكون كائن العميل دائمًا في حالة صالحة(valid state)‏، على العكس من ذلك هو أن تكون بعض مراجع التبعية(dependency references)‏ الخاصة به خالية(null )‏ (لم يتم تعيينها). ومع ذلك، من تلقاء نفسها، تفتقر إلى المرونة لتغيير تبعياتها في وقت لاحق. يمكن أن تكون هذه خطوة أولى نحو جعل العميل غير قابل للتغيير (immutable)‏ وبالتالي خيط آمن .

// Constructor Client(Service service, Service otherService) { if (service == null) { throw new InvalidParameterException("service must not be null"); } if (otherService == null) { throw new InvalidParameterException("otherService must not be null"); } // Save the service references inside this client this.service = service; this.otherService = otherService; }

مقارنة حقن المعيّن(الضبط)(Setter injection comparison)‏

يتطلب من العميل توفير طريقة ضبطsetter method لكل تبعية. وهذا يعطي حرية التلاعب(manipulate )‏بحالة(state )‏ المراجع التبعية (dependency references)‏ في أي وقت. يوفر هذا المرونة، ولكن إذا كان هناك أكثر من تبعية واحدة يجب حقنها، فمن الصعب على العميل التأكد من حقن جميع التبعيات قبل أن يتم توفير العميل للاستخدام.

// Set the service to be used by this client public void setService(Service service) { if (service == null) { throw new InvalidParameterException("service must not be null"); } this.service = service; } // Set the other service to be used by this client public void setOtherService(Service otherService) { if (otherService == null) { throw new InvalidParameterException("otherService must not be null"); } this.otherService = otherService; }

نظرًا لأن هذه الحقن تحدث بشكل مستقل، فلا توجد طريقة لمعرفة متى ينتهي الحاقن من توصيل( wiring)‏ العميل . يمكن ترك التبعية فارغة(null)‏ببساطة عن طريق الفشل في استدعاء أداة ضبطها(failing to call its setter)‏. هذا يفرض التحقق من اكتمال الحقن من وقت تجميع (assembled)‏ العميل حتى وقت استخدامه.

// Set the service to be used by this client public void setService(Service service) { this.service = service; } // Set the other service to be used by this client public void setOtherService(Service otherService) { this.otherService = otherService; } // Check the service references of this client private void validateState() { if (service == null) { throw new IllegalStateException("service must not be null"); } if (otherService == null) { throw new IllegalStateException("otherService must not be null"); } } // Method that uses the service references public void doSomething() { validateState(); service.doYourThing(); otherService.doYourThing(); }

مقارنة حقن الواجهة (Interface injection comparison)‏

تتمثل ميزة حقن الواجهة (interface injection )‏ في أن التبعيات يمكن أن تكون جاهلة تمامًا بعملائها، ومع ذلك لا يزال بإمكانها تلقي مرجع (reference )‏إلى عميل جديد (new client،)‏ واستخدامه، وإرسال مرجع إلى الذات (reference-to-self )‏ إلى العميل(back to the client)‏. بهذه الطريقة، تصبح التبعيات عن طريق الحقن. الفكرة هي أن طريقة الحقن (injecting method)‏ (التي يمكن أن تكون مجرد طريقة ضبط كلاسيكية(classic setter method)‏) يتم توفيرها من خلال واجهة(interface)‏.

لا يزال هناك حاجة إلى مجمع(assembler )‏لتعريف(introduce )‏العميل وتبعياته. سيأخذ المُجمِّع مرجع (reference )‏إلى العميل، ويحولها(cast )‏ إلى واجهة المحدد(setter interface)‏ التي تعيّن تلك التبعية (sets that dependency،)‏ وتمريرها إلى كائن التبعية(dependency object)‏ هذا الذي يتحول ويمرر مرجع إلى الذات إلى العميل(pass a reference-to-self back to the client)‏. .

لكي يكون لحقن الواجهة قيمة، التبعية يجب أن تقوم بشيء ، بالإضافة إلى إرجاع مرجع لنفسها (back a reference to itself)‏. . يمكن أن يكون هذا بمثابة مصنع(factory )‏ أو مجمع فرعي(sub-assembler)‏ لحل تبعيات أخرى(resolve other dependencies)‏، وبالتالي تجريد(abstracting )‏ بعض التفاصيل من المجمع الرئيسي(main assembler)‏. . يمكن أن يكون عدًا مرجعيًا(reference-counting)‏ حتى تعرف التبعية عدد العملاء الذين يستخدمونه. إذا احتفظت التبعية بمجموعة من العملاء( maintains a collection of clients)‏، فيمكنها لاحقًا حقنهم جميعًا بمثيل مختلف عن نفسها (different instance of itself.)‏

// Service setter interface. public interface ServiceSetter { public void setService(Service service); } // Client class public class Client implements ServiceSetter { // Internal reference to the service used by this client. private Service service; // Set the service that this client is to use. @Override public void setService(Service service) { this.service = service; } } // Injector class public class ServiceInjector { Set<ServiceSetter> clients; public void inject(ServiceSetter client) { clients.add(client); client.setService(new ServiceFoo()); } public void switchToBar() { for (Client client : clients) { client.setService(new ServiceBar()); } } } // Service classes public class ServiceFoo implements Service {} public class ServiceBar implements Service {}

تجميع الأمثلة ( Assembling examples)‏

يعد التجميع (assembling)‏ يدويًا ب main الرئيسية إحدى طرق تنفيذ حقن التبعية (implementing dependency injection.)‏ .

public class Injector { public static void main(String[] args) { // Build the dependencies first Service service = new ExampleService(); // Inject the service, constructor style Client client = new Client(service); // Use the objects System.out.println(client.greet()); } }

ينشئ (constructs)‏ المثال أعلاه الرسم البياني للكائن (object graph)‏ يدويًا ثم يستدعيه (invokes)‏ عند نقطة واحدة لبدء العمل. من المهم ملاحظة أن هذا الحاقن ليس نقيًا (not pure)‏. يستخدم أحد الكائنات التي يقوم ببنائها (one of the objects it constructs)‏. لها علاقة بناء فقط (purely construction-only relationship)‏ مع ExampleService ولكنها تمزج البناء واستخدام العميل (construction and using of Client)‏. لا ينبغي أن يكون هذا شائعًا. ومع ذلك، لا مفر منه. تمامًا مثل البرمجيات الموجهة للكائنات (object oriented software)‏ تحتاج إلى طريقة ثابتة غير موجهة للكائنات (non-object oriented static method)‏مثل ()main للبدء، يحتاج الرسم البياني للكائن المحقون بالتبعية (a dependency injected object graph)‏ إلى نقطة إدخال واحدة (entry point)‏ على الأقل (يفضل واحدة فقط) لبدء كل شيء.

قد لا يكون هذا البناء اليدوي (Manual construction)‏ في الطريقة الرئيسية (main method)‏ مباشرأ (may not be this straight forward)‏ وقد يتضمن استدعاء بناة أو مصانع أو أنماط بناء أخرى (may involve calling builders, factories, or other construction patterns)‏ أيضًا. يمكن أن يكون هذا متقدمًا ومجرّدًا (fairly advanced and abstract)‏ إلى حد ما. يتم تجاوز الخط من حقن التبعية اليدوي ((The line is crossed from manual dependency injection))‏ إلى حقن تبعية الإطار ( framework dependency injection)‏ بمجرد أن كود البناء لم يعد مخصصًا للتطبيق بل أصبح عالميًا ((once the constructing code is no longer custom to the application and is instead universal)‏).[38]

يمكن لإطارات مثل Spring إنشاء (construct )‏ هذه الكائنات نفسها وتوصيلها معًا قبل إرجاع مرجع (before returning a reference)‏ إلى العميل (client)‏. يمكن نقل جميع الإشارة إلى ExampleService الملموسة من الكود إلى بيانات التكوين (configuration data))‏ .

import org.springframework.beans.factory.BeanFactory; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Injector { public static void main(String[] args) { // -- Assembling objects -- // BeanFactory beanfactory = new ClassPathXmlApplicationContext("Beans.xml"); Client client = (Client) beanfactory.getBean("client"); // -- Using objects -- // System.out.println(client.greet()); } }

تسمح إطارات مثل Spring بتفريغ تفاصيل التجميع ((assembly details to be externalized))‏ في ملفات التكوين (( configuration files).)‏ يقوم هذا الكود (أعلاه) ببناء الكائنات توصيلها معًا(( constructs objects and wires them together ))‏ وفقًا لـ Beans.xml (أدناه). لا تزال خدمة (ExampleService)‏ قيد الإنشاء ((still constructed) )‏على الرغم من أنها مذكورة أدناه فقط. يمكن تعريف (defined)‏ الرسم البياني للكائن الطويل والمعقد (A long and complex object graph)‏ بهذه الطريقة، والصنف الوحيد المذكور (only class mentioned)‏ في الكود ستكون هي صنف طريقة الإدخال(entry point method)‏، وهي في هذه الحالة (دالة تحية) ()greet.

<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> <bean id="service" class="ExampleService"> </bean> <bean id="client" class="Client"> <constructor-arg value="service" /> </bean> </beans>

في المثال أعلاه، لم يكن يتعين على العميل والخدمة الخضوع(undergo )‏ لأية تغييرات يوزودها إطار سبرينغ (spring)‏ . يسمح لهم بالبقاء كبوجو(POJOs )‏ بسيطة.[39][40][41] يوضح هذا كيف يمكن لspring أن يربط الخدمات (connect services)‏ والعملاء(clients)‏ الذين يجهلون تمامًا وجودها. لا يمكن قول ذلك إذا تمت إضافة التعليقات التوضيحية(annotations)‏ الخاصة بSpring إلى الأصناف(classes)‏. من خلال منع التعليقات التوضيحية الاستدعاءات الخاصة بسبرينغ((spring specific annotations)‏ من الانتشار بين العديد من الأصناف (classes)‏ ، يظل النظام معتمدًا بشكل فضفاض فقط على سبرينغ(( loosely dependent on spring))‏.

[32] قد يكون هذا مهمًا إذا كان النظام ينوي البقاء على قيد الحياة في سبرينغ (if the system intends to outlive spring)‏ . اختيار الحفاظ على POJOs صافية ( keep POJOs pure)‏ لا يأتي بدون تكلفة. بدلاً من بذل الجهد لتطوير ملفات التكوين المعقدة وصيانتها(( to develop and maintain complex configuration files))‏، من الممكن ببساطة استخدام التعليقات التوضيحية (annotations )‏لوضع علامة على الأصناف ((to mark classes)‏) و ترك سبرينغ يقوم بتتمة العمل. يمكن أن يكون حل التبعيات(Resolving dependencies)‏ بسيطًا إذا اتبعت المصطلح عليه عادةً (convention)‏ مثل المطابقة حسب النوع أو بالاسم(matching by type or by name)‏. هذا هو اختيار الاصطلاح على التكوين(convention over configuration)‏ .[42] ويمكن القول (arguable )‏أيضًا أنه عند إعادة البناء(refactoring )‏إلى إطار(framework )‏آخر، فإن إزالة التعليقات التوضيحية المحددة للإطار(removing framework specific annotations)‏ ستكون جزءًا بسيطًا من المهمة

[43] ويتم الآن توحيدstandardized العديد من التعليقات التوضيحية بالحقنinjection annotations .[44][45]

import org.springframework.beans.factory.BeanFactory; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; public class Injector { public static void main(String[] args) { // Assemble the objects BeanFactory beanfactory = new AnnotationConfigApplicationContext(MyConfiguration.class); Client client = beanfactory.getBean(Client.class); // Use the objects System.out.println(client.greet()); } }

import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; @ComponentScan public class MyConfiguration { @Bean public Client client(ExampleService service) { return new Client(service); } }

@Component public class ExampleService { public String getName() { return " } }

مقارنة التجميعAssembly comparison ()‏

لا تختلف تنفيذات()‏ implementations الحاقن injector()‏ المختلفة (المصانعfactories()‏، مواقع الخدمة()‏ (service locators)، وحاويات حقن التبعية ()‏(dependency injection containers) فيما يتعلق بحقن التبعية. ما يحدث فرقاً هو المكان الذي يسمح لهم باستخدامه. قم بنقل الاستدعاءات إلى المصنع ()‏factory أو محدد الخدمة خارج العميل a service locator out of the client()‏ وإلى الرئيسيةmain()‏ والمفاجئ الرئيسي suddenly main()‏ يجعل حاوية حقن التبعية جيدة إلى حد ماmakes a fairly good dependency injection container()‏..

من خلال نقل كل المعرفة عن الحاقن إلى الخارج، يتم ترك عميل نظيف()‏(a clean client)، خالٍ من المعرفة بالعالم الخارجي()‏(, free of knowledge of the outside world, is left behind) . ومع ذلك، يمكن اعتبار أي كائن يستخدم كائنات أخرى عميلاً considered a client()‏.. الكائن الذي يحتوي على main()‏ ليس استثناء. هذا الكائن الرئيسي ()‏main object لا يستخدم حقن التبعية ()‏(This main object is not using dependency injection). . في الواقع استخدام نمط محدد مواقع الخدمة service locator pattern()‏. لا يمكن تجنب ذلك لأنه يجب أن يتم اختيار تنفذيات الخدمةservice implementations()‏ في مكان ما.

لا يؤدي إخارج التبعيات إلى ملفات التكوين إلى تغيير هذه الحقيقة()‏Externalizing the dependencies into configuration files does not change this fact.. ما يجعل هذه حقاً جزءًا من التصميم الجيد هو أن محدد الخدمةservice locator()‏ لا ينتشر في جميع أنحاء قاعدة الكودthe code base()‏.. يقتصر confined على مكان واحد لكل تطبيقper application()‏. هذا يترك بقية الكود الاساسي متاح لاستخدام حقن التبعية لعملاء (نظيفين) ()‏to make clean clients.

نمط حقن التبعية Dependency Injection Pattern()‏

كانت الأمثلة حتى الآن أمثلة بسيطة للغاية حول إنشاء سلسلة(سلسلة نصية) ()‏constructing a string. ومع ذلك،يمكن أنه يكون نمط حقن التبعية أكثر فائدة عند إنشاء رسم بيانيconstructing an object graph()‏ للكائن حيث تتواصل الكائنات عبر الرسائل objects communicate via messages()‏.

ستستمر الكائنات التي تم إنشاؤها في main()‏ طوال عمر البرنامج()‏will last for the life of the program.. النمط النموذجي ()‏typical patternهو إنشاء الرسم البياني()‏construct the graph ثم استدعاء طريقة واحدة على كائن واحد call one method on one object()‏ لإرسال تدفق التحكمto send ()‏the flow of control في الرسم البياني للكائن into the object graph()‏.. تمامًا كما في main()‏ أن نقطة الدخول إلى الكود الثابت()‏(Just as main is the entry point to the static code)، فإن هذه الطريقة method()‏ هي نقطة الدخول is the entry point()‏ إلى الكود غير الثابت للتطبيقات()‏applications non-static code.

.

public static void main(String[] args) throws IOException { // Construction code. Greeter greeter = new Greeter(System.out); // This may be many lines that connect many objects // Behavior code. greeter.greet(); // This is one call to one method on one object in the object graph } class Greeter { public void greet() { this.out.println("Hello world!"); } public Greeter(PrintStream out) { this.out = out; } private PrintStream out; }

مثال AngularJS()‏

في إطار عمل أنغولار (framework AngularJS) ، هناك ثلاث طرقways()‏ فقط يمكن للمكون()‏component (الكائن أو الوظيفةobject or function()‏) الوصول مباشرةً ()‏directly access إلى تبعياته:

  1. يمكن للمكونcomponent()‏ إنشاء التبعية ()‏create the dependency ، عادةً باستخدام عامل ()‏new operator.
  2. يمكن للمكون component()‏ أن يبحث عن التبعية بالإشارة referring()‏ إلى متغير عامglobal variable()‏..
  3. يمكن أن ينتقل المكونThe component can have the dependency passed()‏ إلى التبعية إليه عند الحاجة..

أول خيارين لإنشاء التبعيات أو البحث عنها()‏creating or looking up dependencies ليسا الأمثل لأنهما يكودا التبعية بشكل ثابتhard code the dependency()‏ للمكونto the component()‏

. وهذا يجعل من الصعب، إن لم يكن من المستحيل، تعديل التبعيات. هذا يمثل مشكلة خاصة في الاختبارات ()‏tests، حيث من المستحسن في الغالب توفير تبعيات وهمية provide mock dependencies()‏لعزل الاختبارtest isolation()‏..

الخيار الثالث هو الأكثر قابلية للتطبيق، لأنه يزيل مسؤولية تحديد موقع()‏ locating التبعية من المكونcomponent()‏. يتم تسليم handed()‏ التبعيةdependency ()‏ببساطة إلى المكونcomponent()‏.

function SomeClass(greeter) { this.greeter = greeter; } SomeClass.prototype.doSomething = function(name) { this.greeter.greet(name); }

في المثال أعلاه، لا تهتم ()‏SomeClass بإنشاء أو تحديد مكانlocating()‏ تبعية المحيي ()‏greeter dependency، بل يتم ببساطة تسليم المحيي ()‏handed the greeter عند إنشاء مثيل لهاinstantiated()‏. هذا أمر مرغوب فيه desirable()‏، ولكنه يضع مسؤولية الحصول على التبعية ()‏responsibility of getting hold of the dependency على الكود الذي يبني constructs ()‏ SomeClass .

لإدارة مسؤولية إنشاء التبعية()‏ To manage the responsibility of dependency creation، يحتوي كل تطبيق()‏ AngularJS على حاقن injector()‏. الحاقن هو محدد مواقع الخدمة injector is a service locator()‏ المسؤول عن البناءconstruction()‏ والبحث عن التبعيات.

فيما يلي مثال على استخدام خدمة الحاقن ()‏injector service:

// Provide the wiring information in a module var myModule = angular.module('myModule', []); // Teach the injector how to build a greeter service. // greeter is dependent on the $window service. // The greeter service is an object that // contains a greet method. myModule.factory('greeter', function($window) { return { greet: function(text) { $window.alert(text); } }; });

قم بإنشاء حاقن جديد ()‏Create a new injector يمكنه توفير المكونات المعرفة defined()‏ في نموذج ()‏myModule وطلب خدمة المحيي()‏ greeter service من الحاقن. (يتم ذلك تلقائيًا تلقائيًا عن طريق أنغولار جي اس بووتستراب ()‏AngularJS bootstrap)

var injector = angular.injector(['myModule', 'ng']); var greeter = injector.get('greeter');

إن طلب التبعياتAsking for dependencies يحل مشكلة التكويد الصلب hard coding، ولكنه يعني أيضًا أنه يجب تمرير الحاقن في كل التطبيق. تمرير الحاقن Passing the injector يخالف قانون ديميتر Law of Demeter . لعلاج هذا To remedy this، نستخدم تدوينًا توضيحيًا declarative notation في قوالب HTML الخاصة بنا، لتسليم مسؤولية إنشاء المكوناتcreating components إلى الحاقنinjector، كما في هذا المثال:

<div ng-controller="MyController"> <button ng-click="sayHello()">Hello</button> </div>

function MyController($scope, greeter) { $scope.sayHello = function() { greeter.greet('Hello World'); }; }

عندما يقوم()‏ AngularJS بتجميع()‏(compiles )HTML، فإنه يعالج توجيه(()‏directive) ng-controller ، والذي بدوره يطلب من الحاقن إنشاء مثيل من وحدة التحكم ()‏to create an instance of the controller وتبعياتها

.

injector.instantiate(MyController);

كل هذا يتم خلف الكواليس. نظرًا لأن ng-controller يشير للحاقن لتمثيل

()‏instantiation صنف()‏ class ، فيمكنها تلبية جميع تبعيات MyController دون أن تعرف وحدة التحكمcontroller()‏ عن الحاقن. يعلن ()‏declares كود التطبيق ببساطة التبعيات التي يحتاجها ()‏The application code simply declares the، دون الحاجة إلى التعامل مع الحاقن()‏injector. هذا الإعداد setup()‏ لا يخالف قانون ديميتر ()‏Law of Demeter. .

سي شارب#C

مثال على حقن المنشئ ()‏Constructor injection، حقن المعيّن Setter injection()‏ وحقن الواجهة Interface injection()‏ على(# C)

using System; namespace DependencyInjection { // An interface for the library interface IGamepadFunctionality { String GetGamepadName(); void SetVibrationPower(float InPower); } // Concrete implementation of the xbox controller functionality class XBoxGamepad : IGamepadFunctionality { readonly String GamepadName = "XBox Controller"; float VibrationPower = 1.0f; public String GetGamepadName() => GamepadName; public void SetVibrationPower(float InPower) => VibrationPower = Math.Clamp(InPower, 0.0f, 1.0f); } // Concrete implementation of the playstation controller functionality class PlaystationJoystick : IGamepadFunctionality { readonly String ControllerName = "Playsation joystick"; float VibratingPower = 100.0f; public String GetGamepadName() => ControllerName; public void SetVibrationPower(float InPower) => VibratingPower = Math.Clamp(InPower * 100.0f, 0.0f, 100.0f); } // Concrete implementation of the steam controller functionality class SteamController : IGamepadFunctionality { readonly String JoystickName = "Steam controller"; double Vibrating = 1.0; public String GetGamepadName() => JoystickName; public void SetVibrationPower(float InPower) => Vibrating = Convert.ToDouble(Math.Clamp(InPower, 0.0f, 1.0f)); } // An interface for gamepad functionality injections interface IGamepadFunctionalityInjector { void InjectFunctionality(IGamepadFunctionality InGamepadFunctionality); } class CGamepad : IGamepadFunctionalityInjector { IGamepadFunctionality _GamepadFunctionality; public CGamepad() { } // Constructor injection public CGamepad(IGamepadFunctionality InGamepadFunctionality) => _GamepadFunctionality = InGamepadFunctionality; // Setter injection public void SetGamepadFunctionality(IGamepadFunctionality InGamepadFunctionality) => _GamepadFunctionality = InGamepadFunctionality; // Interface injection public void InjectFunctionality(IGamepadFunctionality InGamepadFunctionality) => _GamepadFunctionality = InGamepadFunctionality; public void Showcase() { String Message = String.Format("We're using the {0} right now, do you want to change the vibrating power?\r\n", _GamepadFunctionality.GetGamepadName()); Console.WriteLine(Message); } } enum EPlatforms: byte { Xbox, Playstation, Steam } class CGameEngine { EPlatforms _Platform; CGamepad _Gamepad; public void SetPlatform(EPlatforms InPlatform) { _Platform = InPlatform; switch(_Platform) { case EPlatforms.Xbox: // injects dependency on XBoxGamepad class through Constructor Injection _Gamepad = new CGamepad(new XBoxGamepad()); break; case EPlatforms.Playstation: _Gamepad = new CGamepad(); // injects dependency on PlaystationJoystick class through Setter Injection _Gamepad.SetGamepadFunctionality(new PlaystationJoystick()); break; case EPlatforms.Steam: _Gamepad = new CGamepad(); // injects dependency on SteamController class through Interface Injection _Gamepad.InjectFunctionality(new SteamController()); break; } _Gamepad.Showcase(); } } class Program { static void Main(string[] args) { Console.WriteLine("Hello World!"); CGameEngine Engine = new CGameEngine(); Engine.SetPlatform(EPlatforms.Steam); Engine.SetPlatform(EPlatforms.Xbox); Engine.SetPlatform(EPlatforms.Playstation); } } }

ملحق: مسرد المصطلحات الإنجليزية

مَسرد المفردات وفق أقسام المقالة
المقدمة
هندسة البرمجيات software engineering
يتلقى فيها كائن كائنات أخرى Object receives other objects
التبعيات dependencies
في العلاقة النموذجية "باستخدام" In the typical "using" relationship
عميل (client)
المحقون injected
خدمة service
يمكن أن يكون الكود الذي ينقل الخدمة إلى العميل أنواعًا كثيرة ويسمى الحاقن can be many kinds of things and is called the injector
الحاقن injector
تمرير التبعية passing of a dependency
الكائن (العميل) client object
حالة العميل client's state
بناء build
العثور عليها find the service
شرطاً أساسياً للنمط fundamental requirement of the pattern
النية intent
حقن التبعية dependency injection
فصل الاهتمامات [[:en:Separation of concerns|separation of concerns]]
زيادة إمكانية القراءة increase readability
إعادة استخدام الكود code reuse
شكل من أشكال التقنية الأوسع لعكس التحكم broader technique of inversion of control
استدعاء بعض الخدمات call some services
معرفة كيفية إنشاء هذه الخدمات to know how to construct those service
يفوض delegates
للكود الخارجي (الحاقن) external code (the injector)
لا يُسمح للعميل باستدعاء كود الحاقن The client is not allowed to call the injector code
الحاقن هو الذي يبني الخدمات it is the injector that constructs the services
تمرير passes
قد يتم بناؤها بواسطة الحاقن be constructed by the injector
كيفية إنشاء الخدمات how to construct the services
حتى الخدمات الفعلية التي يستخدمها even which actual services it is using
معرفة الواجهات الجوهرية للخدمات intrinsic interfaces of the services
يفصل مسؤولية "الاستخدام" "responsibility of "use
مسؤولية "البناء" "responsibility of "construction
نوايا
التطبيق application
الصنف class
مستقل عن كيفية إنشاء كائناته be independent of how its objects are created
طريقة إنشاء الكائنات way objects are created

في ملفات تكوين منفصلة

reated be specified in separate configuration files
تكوينات configurations
دعم support
إنشاء كائنات مباشرة داخل الصنف Creating objects directly within the class
التمثيل instantiation

يوقف الصنف عن إمكانية إعادة استخدامه

It stops the class from being reusable

ويجعل من الصعب اختبار الصنف

(it makes the class hard to test class)‏

لا يمكن استبدال الكائنات الحقيقية بكائنات وهمية

because real objects can not be replaced with mock objects



مقالات ذات صلة

المراجع

  1. Component Relationship - an overview | ScienceDirect Topics - تصفح: نسخة محفوظة 2020-05-23 على موقع واي باك مشين.
  2. I.T., Titanium. "James Shore: Dependency Injection Demystified". www.jamesshore.com. مؤرشف من الأصل في 09 مارس 202018 يوليو 2015.
  3. "HollywoodPrinciple". c2.com. مؤرشف من الأصل في 07 أكتوبر 201619 يوليو 2015.
  4. "The Dependency Injection design pattern - Problem, Solution, and Applicability". w3sDesign.com. مؤرشف من الأصل في 23 مايو 202012 أغسطس 2017.
  5. Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides (1994). Design Patterns: Elements of Reusable Object-Oriented Software. Addison Wesley. صفحات 87ff.  . مؤرشف من الأصل في 23 مايو 2020.
  6. Seeman, Mark (October 2011). Dependency Injection in .NET. Manning Publications. صفحة 4.  .
  7. "Dependency Injection in NET" ( كتاب إلكتروني PDF ). philkildea.co.uk. صفحة 4. مؤرشف من الأصل ( كتاب إلكتروني PDF ) في 04 مارس 201618 يوليو 2015.
  8. "How to explain dependency injection to a 5-year-old?". stackoverflow.com. مؤرشف من الأصل في 25 أبريل 202018 يوليو 2015.
  9. Seemann, Mark. "Dependency Injection is Loose Coupling". blog.ploeh.dk. مؤرشف من الأصل في 19 فبراير 202028 يوليو 2015.
  10. Niko Schwarz, Mircea Lungu, Oscar Nierstrasz, “Seuss: Decoupling responsibilities from static methods for fine-grained configurability”, Journal of Object Technology, Volume 11, no. 1 (April 2012), pp. 3:1-23
  11. "Passing Information to a Method or a Constructor (The Java™ Tutorials > Learning the Java Language > Classes and Objects)". docs.oracle.com. مؤرشف من الأصل في 16 مايو 202018 يوليو 2015.
  12. "A curry of Dependency Inversion Principle (DIP), Inversion of Control (IoC), Dependency Injection (DI) and IoC Container - CodeProject". www.codeproject.com. مؤرشف من الأصل في 19 فبراير 202008 أغسطس 2015.
  13. "How to force "program to an interface" without using a java Interface in java 1.6". programmers.stackexchange.com. مؤرشف من الأصل في 11 أغسطس 201619 يوليو 2015.
  14. "To "new" or not to "new"…". مؤرشف من الأصل في 13 مايو 202018 يوليو 2015.
  15. "How to write testable code". www.loosecouplings.com. مؤرشف من الأصل في 19 فبراير 202018 يوليو 2015.
  16. "Writing Clean, Testable Code". www.ethanresnick.com. مؤرشف من الأصل في 19 فبراير 202018 يوليو 2015.
  17. Sironi, Giorgio. "When to inject: the distinction between newables and injectables - Invisible to the eye". www.giorgiosironi.com. مؤرشف من الأصل في 19 فبراير 202018 يوليو 2015.
  18. "Inversion of Control vs Dependency Injection". stackoverflow.com. مؤرشف من الأصل في 23 أبريل 202005 أغسطس 2015.
  19. "What is the difference between Strategy pattern and Dependency Injection?". stackoverflow.com. مؤرشف من الأصل في 04 يناير 201718 يوليو 2015.
  20. "Dependency Injection != using a DI container". www.loosecouplings.com. مؤرشف من الأصل في 24 فبراير 202018 يوليو 2015.
  21. "Black Sheep » DIY-DI » Print". blacksheep.parry.org. مؤرشف من الأصل في 27 يونيو 201518 يوليو 2015.
  22. 3.1. Dependency injection — Python 3: From None to Machine Learning - تصفح: نسخة محفوظة 2020-05-23 على موقع واي باك مشين.
  23. http://python-dependency-injector.ets-labs.org/introduction/di_in_python.html - تصفح: نسخة محفوظة 2019-11-16 على موقع واي باك مشين.
  24. https://visualstudiomagazine.com/articles/2014/07/01/larger-applications.aspx - تصفح: نسخة محفوظة 2019-03-20 على موقع واي باك مشين.
  25. "The Java Community Process(SM) Program - JSRs: Java Specification Requests - detail JSR# 330". jcp.org. مؤرشف من الأصل في 15 مايو 202018 يوليو 2015.
  26. https://dzone.com/articles/how-dependency-injection-di-works-in-spring-java-a - تصفح: نسخة محفوظة 2019-12-31 على موقع واي باك مشين.
  27. "the urban canuk, eh: On Dependency Injection and Violating Encapsulation Concerns". www.bryancook.net. مؤرشف من الأصل في 06 نوفمبر 201918 يوليو 2015.
  28. "The Dependency Injection Design Pattern". msdn.microsoft.com. مؤرشف من الأصل في 02 يوليو 201518 يوليو 2015.
  29. https://www.freecodecamp.org/news/a-quick-intro-to-dependency-injection-what-it-is-and-when-to-use-it-7578c84fa88f/ - تصفح: نسخة محفوظة 2020-01-16 على موقع واي باك مشين.
  30. https://www.professionalqa.com/dependency-injection - تصفح: نسخة محفوظة 2017-03-05 على موقع واي باك مشين.
  31. "What are the downsides to using Dependency Injection?". stackoverflow.com. مؤرشف من الأصل في 25 أبريل 202018 يوليو 2015.
  32. "Dependency Injection Inversion - Clean Coder". sites.google.com. مؤرشف من الأصل في 03 ديسمبر 201618 يوليو 2015.
  33. "Decoupling Your Application From Your Dependency Injection Framework". InfoQ. مؤرشف من الأصل في 06 أكتوبر 201718 يوليو 2015.
  34. "The Dependency Injection design pattern - Structure and Collaboration". w3sDesign.com. مؤرشف من الأصل في 23 مايو 202012 أغسطس 2017.
  35. Martin Fowler (2004-01-23). "Inversion of Control Containers and the Dependency Injection pattern - Forms of Dependency Injection". Martinfowler.com. مؤرشف من الأصل في 21 مايو 202022 مارس 2014.
  36. "Yan - Dependency Injection Types". Yan.codehaus.org. مؤرشف من الأصل في 18 أغسطس 201311 ديسمبر 2013.
  37. "AccessibleObject (Java Platform SE 7)". docs.oracle.com. مؤرشف من الأصل في 18 يناير 201918 يوليو 2015.
  38. Riehle, Dirk (2000), Framework Design: A Role Modeling Approach ( كتاب إلكتروني PDF ), Swiss Federal Institute of Technology, مؤرشف من الأصل ( كتاب إلكتروني PDF ) في 9 يناير 2019
  39. "Spring Tips: A POJO with annotations is not Plain". مؤرشف من الأصل في 15 يوليو 201518 يوليو 2015.
  40. "Annotations in POJO – a boon or a curse? | Techtracer". 2007-04-07. مؤرشف من الأصل في 26 أبريل 201918 يوليو 2015.
  41. Pro Spring Dynamic Modules for OSGi Service Platforms. APress. 2009-02-17.  . مؤرشف من الأصل في 23 مايو 202006 يوليو 2015.
  42. "Captain Debug's Blog: Is 'Convention Over Configuration' Going Too Far?". www.captaindebug.com. مؤرشف من الأصل في 20 أغسطس 201918 يوليو 2015.
  43. Decker, Colin. "What's the issue with @Inject? | Colin's Devlog". blog.cgdecker.com. مؤرشف من الأصل في 05 مارس 201918 يوليو 2015.
  44. Morling, Gunnar (2012-11-18). "Dagger - A new Java dependency injection framework". Dagger - A new Java dependency injection framework - Musings of a Programming Addict. مؤرشف من الأصل في 05 نوفمبر 201918 يوليو 2015.
  45. "The Java Community Process(SM) Program - JSRs: Java Specification Requests - detail JSR# 330". www.jcp.org. مؤرشف من الأصل في 15 مايو 202018 يوليو 2015.

روابط خارجية

موسوعات ذات صلة :