مستخدم:Mahmoud2013/حقن التبعية
في هندسة البرمجيات ، يعد حقن التبعية (بالإنجليزية: dependency injection) تقنية يتلقى فيها كائن كائنات أخرى يعتمد عليها . تسمى هذه الكائنات الأخرى التبعيات.
في العلاقة النموذجية "باستخدام" [1] يسمى الكائن المتلقي عميل (بالإنجليزية: client) ويسمى الكائن الذي تم تمريره (أي تم حقنه) خدمة (بالإنجليزية: service) . يمكن أن يكون الكود الذي ينقل الخدمة إلى العميل أنواعًا كثيرة ويسمى الحاقن . بدلاً من تحديد العميل للخدمة التي سيستخدمها، يخبر الحاقن العميل بالخدمة التي سيستخدمها. تشير "الحقن" إلى تمرير التبعية (خدمة) إلى الكائن (العميل) الذي قد يستخدمها.
الخدمة تصبح جزءاً من حالة العميل [2]. يعد تمرير الخدمة إلى العميل، بدلاً من السماح للعميل ببناء الخدمة أو العثور عليها، شرطاً أساسياً للنمط.
النية من حقن التبعية هو تحقيق فصل الاهتمامات الخاصة بالبناء واستخدام الكائنات . يمكن أن يؤدي ذلك إلى زيادة إمكانية القراءة وإعادة استخدام الكود.
حقن التبعية هو شكل من أشكال التقنية الأوسع لعكس التحكم . لا يجب على العميل الذي يريد استدعاء بعض الخدمات معرفة كيفية إنشاء هذه الخدمات . بدلاً من ذلك، يفوض العميل مسؤولية توفير خدماته للكود الخارجي (الحاقن). لا يُسمح للعميل باستدعاء كود الحاقن[3] ؛ الحاقن هو الذي يبني الخدمات. ثم يقوم الحاقن بحقن (تمرير) الخدمات في العميل التي قد تكون موجودة بالفعل أو قد يتم بناؤها بواسطة الحاقن. ثم يستخدم العميل الخدمات . هذا يعني أن العميل لا يحتاج إلى معرفة الحاقن ، وكيفية إنشاء الخدمات، أو حتى الخدمات الفعلية التي يستخدمها. يحتاج العميل فقط إلى معرفة الواجهات الجوهرية للخدمات لأن هذه تحدد كيفية استخدام العميل للخدمات . وهذا يفصل مسؤولية "الاستخدام" (بالإنجليزية: "use") عن مسؤولية "البناء" (بالإنجليزية: "construction").
نوايا
[عدل]حقن التبعية يحل مشاكل مثل:[4]
- كيف يمكن أن يكون التطبيق أو الصنف مستقل عن كيفية إنشاء كائناته ؟
- كيف يمكن تحديد طريقة إنشاء الكائنات في ملفات تكوين منفصلة ؟
- كيف يمكن لتطبيق دعم تكوينات مختلفة؟
إنشاء كائنات مباشرة داخل الصنف أمر غير مرن لأنه يلزم الصنف بكائنات معينة ويجعل من المستحيل تغيير التمثيل الفوري بشكل مستقل عن الصنف(دون الحاجة إلى تغيير) . يوقف الصنف عن إمكانية إعادة استخدامه
إذا كانت هناك حاجة إلى كائنات أخرى، ويجعل من الصعب اختبار الصنف لأنه لا يمكن استبدال الكائنات الحقيقية بكائنات وهمية .
لم يعد الصنف مسؤول عن إنشاء الكائنات التي يتطلبها، ولا يتعين عليه تفويض إنشاء مثيل لكائن مصنع كما هو الحال في نمط تصميم [5] .
انظر أيضًا صنف UML ومخطط التسلسل أدناه.
نظرة عامة
[عدل]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.حقن التبعية يفصل إنشاء تبعيات العميل عن سلوك العميل ، والذي يسمح لتصاميم البرنامج أن تكون مقترنة بشكل متساهل [9]
ومتابعة انعكاس التبعية و مبادئ المسؤولية واحدة [10]. يتناقض بشكل مباشر مع نمط محدد مواقع الخدمة، والذي يسمح للعملاء بمعرفة النظام الذي يستخدمونه للعثور على التبعيات. .الحقن، الوحدة الأساسية لحقن التبعية ، ليست آلية جديدة أو مخصصة . يعمل بنفس الطريقة التي يعمل بها " تمرير المعلمة".[11]
بالإشارة إلى "تمرير المعلمة" حيث أن الحقن يحمل ضمناً إضافياً أنه يتم القيام به لعزل العميل عن التفاصيل.
إن الحقن (بالإنجليزية: 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 (بالإنجليزية: 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 إلى تبعياته:
- يمكن للمكونcomponent(بالإنجليزية: ) إنشاء التبعية (بالإنجليزية: )create the dependency ، عادةً باستخدام عامل (بالإنجليزية: )
new
operator. - يمكن للمكون component(بالإنجليزية: ) أن يبحث عن التبعية بالإشارة referring(بالإنجليزية: ) إلى متغير عامglobal variable(بالإنجليزية: )..
- يمكن أن ينتقل المكون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. .
مثال على حقن المنشئ (بالإنجليزية: )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 |
تفويض | delegate |
مثيل | instantiation |
لكائن مصنع | to a factory object |
نمط تصميم | Abstract Factory |
نظرة عامة | |
إنشاء تبعيات العميل | creation of a client's dependencies |
سلوك العميل | client's behavior |
لتصاميم البرنامج أن تكون مقترنة بشكل متساهل | program designs to be loosely coupled |
متابعة انعكاس التبعية | to follow the dependency inversion |
مبادئ المسؤولية واحدة | single responsibility principles |
يتناقض بشكل مباشر مع نمط محدد مواقع الخدمة | 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" |
يحمل | carries |
ضمناً إضافياً | the added implication |
يتم القيام به لعزل العميل عن التفاصيل | to isolate the client from details |
انظر أيضا
[عدل]- لغة وصف العمارةArchitecture description language
- نمط المصنعFactory pattern
- قلب السيطرةInversion of control
- المكون الإضافي (الحوسبة)Plug-in (computing)
- نمط الاستراتيجيةStrategy pattern
- AngularJS
- نمط محدد مواقع الخدمةService locator pattern
- معلمة (برمجة الكمبيوتر)Parameter (computer programming)
- QuajectQuaject
المراجع
[عدل]- ^ Component Relationship - an overview | ScienceDirect Topics نسخة محفوظة 2020-05-23 على موقع واي باك مشين.
- ^ I.T.، Titanium. "James Shore: Dependency Injection Demystified". www.jamesshore.com. مؤرشف من الأصل في 2020-03-09. اطلع عليه بتاريخ 2015-07-18.
- ^ "HollywoodPrinciple". c2.com. مؤرشف من الأصل في 2016-10-07. اطلع عليه بتاريخ 2015-07-19.
- ^ "The Dependency Injection design pattern - Problem, Solution, and Applicability". w3sDesign.com. مؤرشف من الأصل في 2020-05-23. اطلع عليه بتاريخ 2017-08-12.
- ^
Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides (1994). Design Patterns: Elements of Reusable Object-Oriented Software. Addison Wesley. ص. 87ff. ISBN:0-201-63361-2. مؤرشف من الأصل في 2020-05-23.
{{استشهاد بكتاب}}
: صيانة الاستشهاد: أسماء متعددة: قائمة المؤلفين (link) - ^ Seeman، Mark (أكتوبر 2011). Dependency Injection in .NET. Manning Publications. ص. 4. ISBN:9781935182504.
- ^ "Dependency Injection in NET" (PDF). philkildea.co.uk. ص. 4. مؤرشف من الأصل (PDF) في 2016-03-04. اطلع عليه بتاريخ 2015-07-18.
- ^ "How to explain dependency injection to a 5-year-old?". stackoverflow.com. مؤرشف من الأصل في 2020-04-25. اطلع عليه بتاريخ 2015-07-18.
- ^ Seemann، Mark. "Dependency Injection is Loose Coupling". blog.ploeh.dk. مؤرشف من الأصل في 2020-02-19. اطلع عليه بتاريخ 2015-07-28.
- ^ 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
- ^ "Passing Information to a Method or a Constructor (The Java™ Tutorials > Learning the Java Language > Classes and Objects)". docs.oracle.com. مؤرشف من الأصل في 2020-05-16. اطلع عليه بتاريخ 2015-07-18.
- ^ "A curry of Dependency Inversion Principle (DIP), Inversion of Control (IoC), Dependency Injection (DI) and IoC Container - CodeProject". www.codeproject.com. مؤرشف من الأصل في 2020-02-19. اطلع عليه بتاريخ 2015-08-08.
- ^ "How to force "program to an interface" without using a java Interface in java 1.6". programmers.stackexchange.com. مؤرشف من الأصل في 2016-08-11. اطلع عليه بتاريخ 2015-07-19.
- ^ "To "new" or not to "new"…". مؤرشف من الأصل في 2020-05-13. اطلع عليه بتاريخ 2015-07-18.
- ^ "How to write testable code". www.loosecouplings.com. مؤرشف من الأصل في 2020-02-19. اطلع عليه بتاريخ 2015-07-18.
- ^ "Writing Clean, Testable Code". www.ethanresnick.com. مؤرشف من الأصل في 2020-02-19. اطلع عليه بتاريخ 2015-07-18.
- ^ Sironi، Giorgio. "When to inject: the distinction between newables and injectables - Invisible to the eye". www.giorgiosironi.com. مؤرشف من الأصل في 2020-02-19. اطلع عليه بتاريخ 2015-07-18.
- ^ "Inversion of Control vs Dependency Injection". stackoverflow.com. مؤرشف من الأصل في 2020-04-23. اطلع عليه بتاريخ 2015-08-05.
- ^ "What is the difference between Strategy pattern and Dependency Injection?". stackoverflow.com. مؤرشف من الأصل في 2017-01-04. اطلع عليه بتاريخ 2015-07-18.
- ^ ا ب "Dependency Injection != using a DI container". www.loosecouplings.com. مؤرشف من الأصل في 2020-02-24. اطلع عليه بتاريخ 2015-07-18.
- ^ "Black Sheep » DIY-DI » Print". blacksheep.parry.org. مؤرشف من الأصل في 2015-06-27. اطلع عليه بتاريخ 2015-07-18.
- ^ 3.1. Dependency injection — Python 3: From None to Machine Learning نسخة محفوظة 2020-05-23 على موقع واي باك مشين.
- ^ http://python-dependency-injector.ets-labs.org/introduction/di_in_python.html نسخة محفوظة 2019-11-16 على موقع واي باك مشين.
- ^ https://visualstudiomagazine.com/articles/2014/07/01/larger-applications.aspx نسخة محفوظة 2019-03-20 على موقع واي باك مشين.
- ^ "The Java Community Process(SM) Program - JSRs: Java Specification Requests - detail JSR# 330". jcp.org. مؤرشف من الأصل في 2020-05-15. اطلع عليه بتاريخ 2015-07-18.
- ^ ا ب ج https://dzone.com/articles/how-dependency-injection-di-works-in-spring-java-a نسخة محفوظة 2019-12-31 على موقع واي باك مشين.
- ^ "the urban canuk, eh: On Dependency Injection and Violating Encapsulation Concerns". www.bryancook.net. مؤرشف من الأصل في 2019-11-06. اطلع عليه بتاريخ 2015-07-18.
- ^ "The Dependency Injection Design Pattern". msdn.microsoft.com. مؤرشف من الأصل في 2015-07-02. اطلع عليه بتاريخ 2015-07-18.
- ^ https://www.freecodecamp.org/news/a-quick-intro-to-dependency-injection-what-it-is-and-when-to-use-it-7578c84fa88f/ نسخة محفوظة 2020-01-16 على موقع واي باك مشين.
- ^ https://www.professionalqa.com/dependency-injection نسخة محفوظة 2017-03-05 على موقع واي باك مشين.
- ^ "What are the downsides to using Dependency Injection?". stackoverflow.com. مؤرشف من الأصل في 2020-04-25. اطلع عليه بتاريخ 2015-07-18.
- ^ ا ب "Dependency Injection Inversion - Clean Coder". sites.google.com. مؤرشف من الأصل في 2016-12-03. اطلع عليه بتاريخ 2015-07-18.
- ^ "Decoupling Your Application From Your Dependency Injection Framework". InfoQ. مؤرشف من الأصل في 2017-10-06. اطلع عليه بتاريخ 2015-07-18.
- ^ ا ب "The Dependency Injection design pattern - Structure and Collaboration". w3sDesign.com. مؤرشف من الأصل في 2020-05-23. اطلع عليه بتاريخ 2017-08-12.
- ^ Martin Fowler (23 يناير 2004). "Inversion of Control Containers and the Dependency Injection pattern - Forms of Dependency Injection". Martinfowler.com. مؤرشف من الأصل في 2020-05-21. اطلع عليه بتاريخ 2014-03-22.
- ^ "Yan - Dependency Injection Types". Yan.codehaus.org. مؤرشف من الأصل في 2013-08-18. اطلع عليه بتاريخ 2013-12-11.
- ^ "AccessibleObject (Java Platform SE 7)". docs.oracle.com. مؤرشف من الأصل في 2019-01-18. اطلع عليه بتاريخ 2015-07-18.
- ^ Riehle، Dirk (2000)، Framework Design: A Role Modeling Approach (PDF)، Swiss Federal Institute of Technology، مؤرشف من الأصل (PDF) في 2019-01-09
- ^ "Spring Tips: A POJO with annotations is not Plain". مؤرشف من الأصل في 2015-07-15. اطلع عليه بتاريخ 2015-07-18.
- ^ "Annotations in POJO – a boon or a curse? | Techtracer". 7 أبريل 2007. مؤرشف من الأصل في 2019-04-26. اطلع عليه بتاريخ 2015-07-18.
- ^ Pro Spring Dynamic Modules for OSGi Service Platforms. APress. 17 فبراير 2009. ISBN:9781430216124. مؤرشف من الأصل في 2020-05-23. اطلع عليه بتاريخ 2015-07-06.
- ^ "Captain Debug's Blog: Is 'Convention Over Configuration' Going Too Far?". www.captaindebug.com. مؤرشف من الأصل في 2019-08-20. اطلع عليه بتاريخ 2015-07-18.
- ^ Decker، Colin. "What's the issue with @Inject? | Colin's Devlog". blog.cgdecker.com. مؤرشف من الأصل في 2019-03-05. اطلع عليه بتاريخ 2015-07-18.
- ^ Morling، Gunnar (18 نوفمبر 2012). "Dagger - A new Java dependency injection framework". Dagger - A new Java dependency injection framework - Musings of a Programming Addict. مؤرشف من الأصل في 2019-11-05. اطلع عليه بتاريخ 2015-07-18.
- ^ "The Java Community Process(SM) Program - JSRs: Java Specification Requests - detail JSR# 330". www.jcp.org. مؤرشف من الأصل في 2020-05-15. اطلع عليه بتاريخ 2015-07-18.
روابط خارجية
[عدل]- جذر التكوين بواسطة مارك Seemann
- دليل المبتدئين لحقن التبعية
- حقن التبعية والأشياء القابلة للاختبار: تصميم كائنات متقاربة وقابلة للاختبار - جيريمي ويسكوتن ؛ مجلة دكتور دوب، مايو 2006.
- أنماط التصميم: حقن التبعية - مجلة MSDN ، سبتمبر 2005
- مقالة مارتن فاولر الأصلية التي أدخلت مصطلح حقن التبعية
- P من EAA: البرنامج المساعد
- التراث الهندسي الغني وراء حقن التبعية - أندرو ماكفي - تاريخ مصنف classلحقن التبعية.
- ما هو حقن التبعية؟ - تفسير بديل - جاكوب جينكوف
- كتابة كود أكثر قابلية للاختبار مع حقن التبعية - Developer.com ، أكتوبر 2006
- نظرة عامة على إطار التوسعة المدارة - MSDN
- الوصف القديم لآلية التبعية بواسطة Hunt 1998
- إعادة بناء طريقك إلى حاوية حقن التبعية
- فهم DI في PHP
- لا تحتاج إلى حاوية حقن التبعية
تصنيف:معمارية البرمجيات تصنيف:صفحات بترجمات غير مراجعة تصنيف:نماذج تصميم البرمجيات تصنيف:هندسة البرمجيات بالمكونات