حقن التبعية
المحتويات
- حقن المعيّن
- مقارنة حقن المنشئ
- }
- ملحق: مسرد المصطلحات الإنجليزية
- <?xml version="1.0" encoding="UTF-8"?>
- أنواع أخرى
- }
- }
- break;
- }
- مخطط الصنف UML ومخطط التتابع
- if (otherService == null) {
- نوايا
- // Assemble the objects
- حقن المنشئ
- if (service == null) {
- {
- تجميع الأمثلة
- حقن المعيّن
- {
- حقن الواجهة
- {
- }
- var myModule = angular.module('myModule', []);
- مزايا
- // Constructor injection
- نمط محدد موقع الخدمة
- حقن الواجهة
- مثال أنغولار جي إس
- if (service == null) {
- Client {
- }
- this.otherService = otherService;
- عكس التحكم
- private Service service;
- .injector.instantiate(MyController);
- }
- سي شارب
- // Interface injection
- التصنيف
- }
- }
- import org.springframework.context.ApplicationContext;
- حقن المنشئ
- public String GetGamepadName => GamepadName;
- var injector = angular.injector(['myModule', 'ng']);
- }
- }
- أطر حقن التبعية
- public class Client implements ServiceSetter {
- break;
- _Gamepad = new CGamepad;
- if (otherService == null) {
- if (otherService == null) {
- مقارنة حقن الواجهة
- سلبيات
- if (service == null) {
- });
- // Setter injection
- بدون حقن التبعية
- P من EAA: البرنامج المساعد
- public String getName {
- نمط حقن التبعية
- import org.springframework.context.ApplicationContext;
- public String GetGamepadName => JoystickName;
- _Gamepad = new CGamepad;
- أنواع حقن التبعية
- private Service service;
- public void setService(Service service) {
- CGamepad _Gamepad;
- // -- Assembling objects -- //
- public class Client {
- _Platform = InPlatform;
- دليل المبتدئين لحقن التبعية
- {
- مقارنة التجميع
- public class Client implements ServiceSetter {
- }
- }
- }import org.springframework.context.annotation.Bean;
- Client(Service service) {
- public String GetGamepadName => ControllerName;
- }
- نظرة عامة
قبل الخوض في التفاصيل، في هندسة البرمجيات، يعد حقن التبعية تقنية يتلقى فيها كائن كائنات أخرى يعتمد عليها. تسمى هذه الكائنات الأخرى التبعيات. وفيما يلي تفاصيل أوسع عن ذلك.
ومن ناحية أخرى، النية من حقن التبعية هو تحقيق فصل الاهتمامات الخاصة بالبناء واستخدام الكائنات. يمكن أن يؤدي ذلك إلى زيادة إمكانية القراءة وإعادة استخدام الكود.
وفي السياق ذاته، حقن التبعية هو شكل من أشكال التقنية الأوسع لعكس التحكم. لا يجب على العميل الذي يريد استدعاء بعض الخدمات معرفة كيفية إنشاء هذه الخدمات. بدلاً من ذلك، يفوض العميل مسؤولية توفير خدماته للكود الخارجي (الحاقن). لا يُسمح للعميل باستدعاء كود الحاقن ؛ الحاقن هو الذي يبني الخدمات. ثم يقوم الحاقن بحقن (تمرير) الخدمات في العميل التي قد تكون موجودة بالفعل أو قد يتم بناؤها بواسطة الحاقن. ثم يستعمل العميل الخدمات. هذا يعني أن العميل لا يحتاج إلى معرفة الحاقن، وكيفية إنشاء الخدمات، أو حتى الخدمات الفعلية التي يستعملها. يحتاج العميل فقط إلى معرفة الواجهات الجوهرية للخدمات لأن هذه تحدد كيفية استخدام العميل للخدمات. وهذا يفصل مسؤولية «الاستخدام» عن مسؤولية «البناء» .
وفي السياق ذاته، في العلاقة النموذجية «باستخدام» يسمى الكائن المتلقي عميل ويسمى الكائن الذي تم تمريره (أي تم حقنه) خدمة . يمكن أن يكون الكود الذي ينقل الخدمة إلى العميل أنواعًا كثيرة ويسمى الحاقن. بدلاً من تحديد العميل للخدمة التي سيستعملها، يخبر الحاقن العميل بالخدمة التي سيستعملها. تشير «الحقن» إلى تمرير التبعية (خدمة) إلى الكائن (العميل) الذي قد يستعملها.
علاوةً على ذلك، الخدمة تصبح جزءاً من حالة العميل. يعد تمرير الخدمة إلى العميل، بدلاً من السماح للعميل ببناء الخدمة أو العثور عليها، شرطاً أساسياً للنمط.
حقن المعيّن
// Setter method
تتطلب هذه الطريقة من العميل توفير طريقة ضبط للتبعية.
مقارنة حقن المنشئ
// Constructor
Client(Service service, Service otherService) {
من جهة ثانية، يُفضل عند إنشاء جميع التبعيات أولاً لأنه يمكن استخدامها لضمان أن يكون كائن العميل دائمًا في حالة صالحة، على العكس من ذلك هو أن تكون بعض مراجع التبعية الخاصة به خالية (لم يتم تعيينها). ومع ذلك، من تلقاء نفسها، تفتقر إلى المرونة لتغيير تبعياتها في وقت لاحق. يمكن أن تكون هذه خطوة أولى نحو جعل العميل غير قابل للتغيير وبالتالي خيط آمن.
}
وفي هذا السياق، } يعتبر حقنالتبعية تقنية بديلة لتهيئة متغير العضو بدلاً من إنشاء كائن خدمة بشكل صريح كما هو موضح أعلاه. يمكننا تعديل هذا المثال
وفي هذا السياق، باستخدام التقنيات المختلفة الموضحة والموضحة في الأقسام الفرعية أدناه..
ملحق: مسرد المصطلحات الإنجليزية
في هذا الإطار، المقدمةهندسة البرمجياتsoftware engineeringيتلقى فيها كائن كائنات أخرىObject receives other objectsالتبعياتdependenciesفي العلاقة النموذجية "باستخدام"In the typical "using" relationshipعميلالمحقون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فصل الاهتمامات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ويجعل من الصعب اختبار الصنفلا يمكن استبدال الكائنات الحقيقية بكائنات وهمية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يخالف قانون ديميتر Law of Demeter.
<?xml version="1.0" encoding="UTF-8"?>
كذلك، قد يكون هذا مهمًا إذا كان النظام ينوي البقاء على قيد الحياة في سبرينغ. اختيار الحفاظ على POJOs صافية لا يأتي بدون تكلفة. بدلاً من بذل الجهد لتطوير ملفات التكوين المعقدة وصيانتها، من الممكن ببساطة استخدام التعليقات التوضيحية لوضع علامة على الأصناف) وترك سبرينغ يقوم بتتمة العمل. يمكن أن يكون حل التبعيات بسيطًا إذا اتبعت المصطلح عليه عادةً مثل المطابقة حسب النوع أو بالاسم. هذا هو اختيار الاصطلاح على التكوين
إضافةً إلى ذلك، . ويمكن القول كذلك أنه عند إعادة البناءإلى إطارآخر، فإن إزالة التعليقات التوضيحية المحددة للإطار ستكون جزءًا بسيطًا من المهمة
وعلى صعيد آخر، http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
إضافةً إلى ذلك، ويتم الآن توحيد الكثير من التعليقات التوضيحية بالحقن. import org.springframework.beans.factory.BeanFactory;
إضافةً إلى ذلك، في المثال أعلاه، لم يكن يتعين على العميل والخدمة الخضوع لأية تغييرات يوزودها إطار سبرينغ. يسمح لهم بالبقاء كبوجو بسيطة. يوضح هذا كيف يمكن لspring أن يربط الخدمات والعملاء الذين يجهلون تمامًا وجودها. لا يمكن قول ذلك إذا تمت إضافة التعليقات التوضيحية الخاصة بSpring إلى الأصناف. من خلال منع التعليقات التوضيحية الاستدعاءات الخاصة بسبرينغ من الانتشار بين الكثير من الأصناف، يظل النظام معتمدًا بشكل فضفاض فقط على سبرينغ.
أنواع أخرى
وفي هذا الصدد، من الممكن أن يكون لأطر حقن التبعية أنواع أخرى من الحقن بخلاف تلك المذكورة أعلاه. قد تستعمل أطر الاختبار كذلك أنواعًا أخرى. بعض أطر الاختبار الحديثة لا تتطلب حتى أن يقبل العملاء بنشاط حقن التبعية وبالتالي جعل الكود القديم قابل للاختبار. على وجه الخصوص، في لغة جافا، من الممكن استخدام الانعكاس لجعل السمات الخاصة عامة عند الاختبار وبالتالي قبول الحقن عن طريق التعيين.
كذلك، لا تقدم بعض محاولات عكس التحكم الإزالة الكاملة للتبعية، ولكن بدلاً من ذلك ببساطة استبدال أحد أشكال التبعية بأخرى. كقاعدة عامة، إذا لم يتمكن المبرمج من النظر إلى شيء سوى كود العميل والإخبار عن إطار العمل المستعمل، عندئذٍ يكون لدى العميل اعتماد مرتبط بالكود الصلب على الإطار.
}
علاوةً على ذلك، تسمح إطارات مثل سبرينغ بتفريغ تفاصيل التجميع في ملفات التكوين يقوم هذا الكود (أعلاه) ببناء الكائنات توصيلها معًا بحسب ـ Beans.xml (أدناه). لا تزال خدمة قيد الإنشاء على الرغم من أنها مذكورة أدناه فقط. يمكن تعريف الرسم البياني للكائن الطويل والمعقد بهذه الطريقة، والصنف الوحيد المذكور في الكود ستكون هي صنف طريقة الإدخال، وهي في هذه الحالة (دالة تحية) greet.
}
إضافةً إلى ذلك، يمكن لإطارات مثل سبرينغ إنشاء هذه الكائنات نفسها وتوصيلها معًا قبل إرجاع مرجع إلى العميل. يمكن نقل جميع الإشارة إلى ExampleService الملموسة من الكود إلى بيانات التكوين.
وفي هذا الصدد، قد لا يكون هذا البناء اليدوي في الطريقة الرئيسية مباشرأ وقد يتضمن استدعاء بناة أو مصانع أو أنماط بناء أخرى كذلك. يمكن أن يكون هذا متقدمًا ومجرّدًا إلى حد ما. يتم تجاوز الخط من حقن التبعية اليدوي إلى حقن تبعية الإطار بمجرد أن كود البناء لم يعد مخصصًا للتطبيق بل أصبح عالميًا).
إضافةً إلى ذلك، ينشئ المثال أعلاه الرسم البياني للكائن يدويًا ثم يستدعيه عند نقطة واحدة لبدء العمل. من المهم ملاحظة أن هذا الحاقن ليس نقيًا. يستعمل أحد الكائنات التي يقوم ببنائها. لها علاقة بناء فقط مع ExampleService ولكنها تمزج البناء واستخدام العميل. لا ينبغي أن يكون هذا شائعًا. ومع ذلك، لا مفر منه. تمامًا مثل البرمجيات الموجهة للكائنات تحتاج إلى طريقة ثابتة غير موجهة للكائنات مثل main للبدء، يحتاج الرسم البياني للكائن المحقون بالتبعية إلى نقطة إدخال واحدة على الأقل (يفضل واحدة فقط) لبدء كل شيء.
import org.springframework.beans.factory.BeanFactory;
break;
case EPlatforms.Playstation:
}
- // Concrete implementation of the playstation controller functionality
- class PlaystationJoystick : IGamepadFunctionality
- {
- readonly String ControllerName = "Playsation joystick";
- float VibratingPower = 100.0f;
مخطط الصنف UML ومخطط التتابع
أمثلة
وفي هذا الصدد، فيUML الرسم التخطيطي لصنف أعلاه، لا يقوم صنف Client الذي يتطلب كائنات ServiceA و ServiceB بإنشاء صنفيServiceA1 و ServiceB1 مباشرة. بدلاً من ذلك، يقوم صنف Injector بإنشاء الكائنات وحقنها في Client، مما يجعل Client مستقلًا عن كيفية إنشاء الكائنات (أي الأصناف الملموسة التي يتم إنشاء مثيل لها). يوضح الرسم التخطيطي لتسلسل UML تفاعلات وقت التشغيل يقوم كائن Injector بإنشاء كائنات ServiceA1 و ServiceB1. لاحقًا، يقوم Injector بإنشاء كائن Client وإدخال كائنات ServiceA1 و ServiceB1.
if (otherService == null) {
ومن جهة أخرى، throw new InvalidParameterException("otherService must not be null");
نوايا
ومن جهة أخرى، إنشاء كائنات مباشرة داخل الصنف أمر غير مرن لأنه يلزم الصنف بكائنات معينة ويجعل من المستحيل تغيير التمثيل الفوري بشكل مستقل عن الصنف (دون الحاجة إلى تغيير). يوقف الصنف عن إمكانية إعادة استخدامه.
- حقن التبعية يحل مشاكل مثل:
- كيف يمكن أن يكون التطبيق أو الصنف مستقل عن كيفية إنشاء كائناته؟
- كيف يمكن تحديد طريقة إنشاء الكائنات في ملفات تكوين منفصلة؟
- كيف يمكن لتطبيق دعم تكوينات مختلفة؟
ومن ناحية أخرى، إذا كانت هناك حاجة إلى كائنات أخرى، ويجعل من الصعب اختبار الصنف لأنه لا يمكن استبدال الكائنات الحقيقية بكائنات وهمية.
انظر كذلك صنف UML ومخطط التسلسل أدناه.
وعلى صعيد آخر، لم يعد الصنف مسؤول عن إنشاء الكائنات التي يتطلبها، ولا يتعين عليه تفويض إنشاء مثيل لكائن مصنع كما هو الحال في نمط تصميم.
// Assemble the objects
من جهة ثانية، BeanFactory beanfactory = new AnnotationConfigApplicationContext(MyConfiguration.class);
- Client client = beanfactory.getBean(Client.class);
- // Use the objects
- System.out.println(client.greet);
- }
حقن المنشئ
// Constructor
تتطلب هذه الطريقة من العميل توفير معلمة في مُنشئ للتبعية.
if (service == null) {
في هذا الإطار، throw new InvalidParameterException("service must not be null");
}
{
علاوةً على ذلك، 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;
تجميع الأمثلة
- يعد التجميع يدويًا داخل الطريقة (الدالة) الرئيسية إحدى طرق تنفيذ حقن التبعية .
- 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);
- }
حقن المعيّن
وفي هذا السياق، يكشف العميل عن طريقة الضبط التي يستعملها الحاقن لحقن التبعية..
{
- case EPlatforms.Xbox:
- // injects dependency on XBoxGamepad class through Constructor Injection
- _Gamepad = new CGamepad(new XBoxGamepad);
حقن الواجهة
وفي هذا السياق، توفر واجهة التبعية طريقة حاقن تضخ التبعية في أي عميل يتم تمريره إليها. يجب على العملاء تنفيذ واجهة تكشف عن طريقة تعيين يقبل التبعية.
{
- 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);
- }
- }
- }
}
// Set the service to be used by this client
public void setService(Service service) {
مقارنة حقن المعيّن (الضبط)
من جانب آخر، يتطلب من العميل توفير طريقة ضبط لكل تبعية. وهذا يعطي حرية التلاعببحالة المراجع التبعية في أي وقت. يوفر هذا المرونة، ولكن إذا كان هناك أكثر من تبعية واحدة يجب حقنها، فمن الصعب على العميل التأكد من حقن جميع التبعيات قبل أن يتم توفير العميل للاستخدام.
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);
- }
- };
مزايا
وفي هذا الصدد، الحد من كود المتداول في كائنات التطبيق، حيث أن الكل يعمل لتهيئة أو إعداد التبعيات يتم معالجته بواسطة مكون المزود.
كذلك، نظرًا لأن حقن التبعية لا يتطلب أي تغيير في سلوك الكود، يمكن تطبيقه على الكود القديم باعتباره إعادة هيكلة الكود.. والنتيجة هي عملاء أكثر استقلالية وأكثر سهولة في اختبار الوحدة في العزل باستخدام كائنات وهمية التي تحاكي كائنات أخرى ليست تحت الاختبار. غالبًا ما تكون سهولة الاختبار هذه أول فائدة ملحوظة عند استخدام حقن التبعية.
إضافةً إلى ذلك، يمكن استخدام حقن التبعية لتجسيد تكوين النظام في ملفات التكوين، مما يسمح بإعادة تكوينالنظام دون إعادة التجميع. يمكن كتابة تكوينات منفصلة التي تتطلب تنفيذات مختلفة للمكوّن. وهذا يشمل، مثلًا لا الحصر، الاختبار.
إضافةً إلى ذلك، يسمح حقن التبعية للعميل بمرونة كونه قابلاً للتكوين. فقط سلوك العميل يتم إصلاحه. قد يقوم العميل بأي شيء يدعم الواجهة الجوهرية التي يتوقعها العميل.
إضافةً إلى ذلك، يسمح حقن التبعية للعميل بإزالة جميع ما هو معروف بالنسبة للتطبيق المحدد الذي يحتاج إلى استخدامه. هذا يساعد على عزل العميل من تأثير تغييرات التصميم والعيوب. يعزز قابلية إعادة الاستخدام والاختبار وقابلية الصيانة.
وفي السياق ذاته، يسمح حقن التبعية بالتطويرالمتزامن أو المستقل. يمكن لمطورين اثنين بشكل مستقل تطوير أصناف تستعمل بعضها البعض، كل ما تحتاجه هو فقط معرفة الواجهة التي ستتواصل من خلالها الاصناف. غالبًا ما يتم تطوير البرنامج المساعدة من خلال متاجر الجهات الخارجية التي لا تتحدث أبدًا مع المطورين الذين أنشؤوا المنتج الذي يستعمل البرنامج المساعد.
يقلل حقن التبعية الاقتران بين صنف والتبعية.
// Constructor injection
من جهة ثانية، public CGamepad(IGamepadFunctionality InGamepadFunctionality) => _GamepadFunctionality = InGamepadFunctionality;
نمط محدد موقع الخدمة
- وسيط (برمجة حاسوب)
- المراجع
- روابط خارجية
- جذر التكوين بواسطة مارك Seemann
حقن الواجهة
- // Service setter interface.
- public interface ServiceSetter {
- public void setService(Service service);
- }
- // Client class
وفي هذا السياق، ببساطة العميل هو الذي ينشر واجهة الدور لطرق تعيين تبعيات العميل. يمكن استخدامه لتحديد الطريقة التي يجب أن يتحدث بها الحاقن مع العميل عند حقن التبعيات.
مثال أنغولار جي إس
وفي السياق ذاته، . وهذا يجعل من الصعب، إن لم يكن من المستحيل، تعديل التبعيات. هذا يمثل مشكلة خاصة في الاختبارات، حيث من المستحسن في الغالب توفير تبعيات وهمية لعزل الاختبار.
- يمكن للمكون إنشاء التبعية، عادةً باستخدام عامل new.
- يمكن للمكون أن يبحث عن التبعية بالإشارة إلى متغير عام.
- يمكن أن ينتقل المكون إلى التبعية إليه عند الحاجة..
- function SomeClass(greeter) {
- this.greeter = greeter;
- }
- SomeClass.prototype.doSomething = function(name) {
- this.greeter.greet(name);
كذلك، أول خيارين لإنشاء التبعيات أو البحث عنها ليسا الأمثل لأنهما يكودا التبعية بشكل ثابت للمكون.
وفي السياق ذاته، الخيار الثالث هو الأكثر قابلية للتطبيق، لأنه يزيل مسؤولية تحديد موقع التبعية من المكون. يتم تسليم التبعية ببساطة إلى المكون.
وفي هذا السياق، في إطار عمل أنغولار، هناك ثلاث طرق فقط يمكن للمكون (الكائن أو الوظيفة) الوصول مباشرةً إلى تبعياته:
if (service == null) {
- throw new InvalidParameterException("service must not be null");
- }
- this.service = service;
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;
this.otherService = otherService;
public void setService(Service service) {
كذلك، } نظرًا لأن هذه الحقن يحدث بشكل مستقل، فلا توجد طريقة لمعرفة متى ينتهي الحاقن من توصيل العميل. يمكن ترك التبعية فارغة ببساطة عن طريق الفشل في استدعاء أداة ضبطها. هذا يفرض التحقق من اكتمال الحقن من وقت تجميع العميل حتى وقت استخدامه. // Set the service to be used by this client
this.service = service;
عكس التحكم
- برنامج مساعد (حوسبة)
- نموذج الإستراتيجية
- أنجولار جي إس
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 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 {}
.injector.instantiate(MyController);
من جهة ثانية، تمثيل صنف، فيمكنه تلبية جميع تبعيات MyController دون أن تعرف وحدة التحكم عن الحاقن. يعلن كود التطبيق ببساطة التبعيات التي يحتاجها، دون الحاجة إلى التعامل مع الحاقن. هذا الإعداد لا يخالف قانون ديميتر.
}
- // Save the service references inside this client
- this.service = service;
- this.otherService = otherService;
سي شارب
- مثال على حقن المنشئ، حقن المعيّن وحقن الواجهة على (#C)
- using System;
- namespace DependencyInjection
- {
- // An interface for the library
- interface IGamepadFunctionality
- {
- String GetGamepadName;
- void SetVibrationPower(float InPower);
// Interface injection
public void Showcase
في هذا الإطار، public void InjectFunctionality(IGamepadFunctionality InGamepadFunctionality) => _GamepadFunctionality = InGamepadFunctionality;
التصنيف
وفي هذا الصدد، يعد عكس التحكم (IoC) أكثر عمومية من حقن التبعية. ببساطة، يعني انعكاس التحكم السماح لكود آخر بالاتصال بك بدلاً من الإصرار على إجراء الاستدعاء. مثال على عكس التحكم بدون حقن التبعية هو نمط طريقة القالب. هنا، يتم تحقيق تعدد الأشكال من خلال التصنيف الفرعي، أي الميراث. يطبق حقن التبعية انعكاس التحكم من خلال التكوين، لذلك غالبًا ما يكون مطابقًا لنمط الإستراتيجية، ولكن في حين أن نمط الإستراتيجية مخصص للتبعيات لتكون قابلة للتبادل طوال عمر الكائن، في حقن التبعية، قد يتم استخدام مثيل واحد فقط من التبعية هذا لا يزال يحقق تعدد الأشكال، ولكن من خلال التفويض والتكوين.
}
- // Concrete implementation of the xbox controller functionality
- class XBoxGamepad : IGamepadFunctionality
- {
- readonly String GamepadName = "XBox Controller";
- float VibrationPower = 1.0f;
}
علاوةً على ذلك، لإدارة مسؤولية إنشاء التبعية، يحتوي كل تطبيق أنغولار جي إس، على حاقن. الحاقن هو محدد مواقع الخدمة المسؤول عن البناء والبحث عن التبعيات.
// Provide the wiring information in a module
من جانب آخر، في المثال أعلاه، لا تهتم SomeClass بإنشاء أو تحديد مكان تبعية المحيي، بل يتم ببساطة تسليم المحيي عند إنشاء مثيل لها. هذا أمر مرغوب فيه، ولكنه يضع مسؤولية الحصول على التبعية على الكود الذي يبني بعض الأصناف SomeClass.
فيما يلي مثال على استخدام خدمة الحاقن:
import org.springframework.context.ApplicationContext;
- import org.springframework.context.support.ClassPathXmlApplicationContext;
- public class Injector {
- public static void main(String[] args) {
حقن المنشئ
يتم توفير التبعيات من خلال مُنشئ صنف العميل..
public String GetGamepadName => GamepadName;
في هذا الإطار، public void SetVibrationPower(float InPower) => VibrationPower = Math.Clamp(InPower, 0.0f, 1.0f);
var injector = angular.injector(['myModule', 'ng']);
- Hello
- function MyController($scope, greeter) {
- $scope.sayHello = function {
- greeter.greet('Hello World');
- };
إضافةً إلى ذلك، var greeter = injector.get('greeter'); إن طلب التبعيات يحل مشكلة التكويد الصلب، ولكنه يعني كذلك أنه يجب تمرير الحاقن في كل التطبيق. تمرير الحاقن يخالف قانون ديميتر. لعلاج هذا، نستعمل تدوينًا توضيحيًا في قوالب HTML الخاصة بنا، لتسليم مسؤولية إنشاء المكونات إلى الحاقن، كما في هذا المثال:
}
- // Concrete implementation of the steam controller functionality
- class SteamController : IGamepadFunctionality
- {
- readonly String JoystickName = "Steam controller";
- double Vibrating = 1.0;
}
ومن ناحية أخرى، عندما يقوم أنغلار جي إس، بتجميع HTML، فإنه يعالج توجيه ng-controller، والذي بدوره يطلب من الحاقن إنشاء مثيل من وحدة التحكم وتبعياتها
أطر حقن التبعية
إضافةً إلى ذلك، أطر التطبيقات مثل CDI وتنفيذها مثل Weld وسبرينغ و Guice واطار عمل بلاي و Salta و Glassfish HK2 و Dagger وإطار التوسعة المدار (MEF) تدعم حقن التبعية ولكن غير الزامية للقيام بحقن التبعية.
public class Client implements ServiceSetter {
// Internal reference to the service used by this client.
break;
case EPlatforms.Steam:
_Gamepad = new CGamepad;
وفي هذا السياق، // injects dependency on PlaystationJoystick class through Setter Injection
_Gamepad.SetGamepadFunctionality(new PlaystationJoystick);
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;
- }
if (otherService == null) {
}
وفي السياق ذاته، throw new InvalidParameterException("otherService must not be null");
مقارنة حقن الواجهة
- // Service setter interface.
- public interface ServiceSetter {
- public void setService(Service service);
- }
- // Client class
ومن جهة أخرى، لا يزال هناك حاجة إلى مجمعلتعريفالعميل وتبعياته. سيأخذ المُجمِّع مرجع إلى العميل، ويحولها إلى واجهة المحدد التي تعيّن تلك التبعية وتمريرها إلى كائن التبعية هذا الذي يتحول ويمرر مرجع إلى الذات إلى العميل..
علاوةً على ذلك، لكي يكون لحقن الواجهة قيمة، التبعية يجب أن تقوم بشيء، إلى جانب إرجاع مرجع لنفسها.. يمكن أن يكون هذا بمثابة مصنع أو مجمع فرعي لحل تبعيات أخرى، وبالتالي تجريد بعض التفاصيل من المجمع الرئيسي.. يمكن أن يكون عدًا مرجعيًا حتى تعرف التبعية عدد العملاء الذين يستعملونه. إذا احتفظت التبعية بمجموعة من العملاء، فيمكنها لاحقًا حقنهم جميعًا بمثيل مختلف عن نفسها
علاوةً على ذلك، تتمثل ميزة حقن الواجهة في أن التبعيات يمكن أن تكون جاهلة تمامًا بعملائها، ومع ذلك لا يزال بإمكانها تلقي مرجع إلى عميل جديد واستخدامه، وإرسال مرجع إلى الذات إلى العميل. بهذه الطريقة، تصبح التبعيات عن طريق الحقن. الفكرة هي أن طريقة الحقن (التي يمكن أن تكون مجرد طريقة ضبط كلاسيكية) يتم توفيرها من خلال واجهة.
سلبيات
وفي هذا الصدد، يتم تنفيذأطر حقن التبعية مع انعكاس أو برمجة ديناميكية. يمكن أن يعيق هذا استخدام أتمتة IDE، مثل "العثور على مراجع" و "إظهار التسلسل الهرمي للاستدعاءات "" وإعادة البناء الآمنة
في هذا الإطار، عادة ما يتطلب حقن التبعية المزيد من جهود التطوير المسبق حيث لا يمكن للمرء أن يستدعي أن يكون شيئًا صحيحًا في الوقت والمكان المطلوبين ولكن يجب أن يطلب حقنه ثم التأكد من أنه تم حقنه.
كذلك، يمكن لحقن التبعية أن يشجع على الاعتماد على إطار حقن التبعية.
هيكل (بناء)
ومن ناحية أخرى، يمكن أن يجعل حقن التبعية من الصعب تتبع الكود (قراءتها) لأنها تفصل بين السلوك والبناء. هذا يعني أنه يجب على المطورين الرجوع إلى المزيد من الملفات لمتابعة كيفية أداء النظام.
وعلى صعيد آخر، يعمل حقن التبعية على إنشاء عملاء يطلبون توفير تفاصيل التكوين عن طريق كود البناء. يمكن أن يكون هذا مرهقًا عندما تتوفر افتراضيات واضحة.
وفي السياق ذاته، يجبر حقن التبعية التعقيد على الانتقال من الأصناف إلى الروابط بين الأصناف التي قد لا تكون دائمًا مرغوبة أو سهلة الإدارة.
if (service == null) {
من جانب آخر، throw new IllegalStateException("service must not be null");
}
});
في هذا الإطار، قم بإنشاء حاقن جديد يمكنه توفير المكونات المعرفة في نموذج myModule وطلب خدمة المحيي من الحاقن. (يتم ذلك تلقائيًا تلقائيًا عن طريق أنغولار جي اس بووتستراب .
// Setter injection
علاوةً على ذلك، public void SetGamepadFunctionality(IGamepadFunctionality InGamepadFunctionality) => _GamepadFunctionality = InGamepadFunctionality;
بدون حقن التبعية
ومن ناحية أخرى، في مثال جافا التالي، يحتوي صنف على خدمة متغير عضو تمت تهيئته بواسطة مُنشئ.
وفي هذا الصدد، يتحكم العميل في تنفيذ الخدمة المستعملة ويتحكم في بنائها. في هذه الحالة، يُقال أن العميل لديه تبعية ذات كود صلب على.
// An example without dependency injection
P من EAA: البرنامج المساعد
- ما هو حقن التبعية؟ - تفسير بديل - جاكوب جينكوف
- كتابة كود أكثر قابلية للاختبار مع حقن التبعية - Developer.com، أكتوبر 2006
- نظرة عامة على إطار التوسعة المدارة - MSDN
- الوصف القديم لآلية التبعية بواسطة Hunt 1998
- إعادة بناء طريقك إلى حاوية حقن التبعية
- فهم DI في PHP
علاوةً على ذلك، التراث الهندسي الغني وراء حقن التبعية - أندرو ماكفي - تاريخ مصنف classلحقن التبعية.
public String getName {
- return "
- }
- }
نمط حقن التبعية
في هذا الإطار، كانت الأمثلة حتى الآن أمثلة بسيطة للغاية حول إنشاء سلسلة (سلسلة نصية). ومع ذلك، يمكن أنه يكون نمط حقن التبعية أكثر فائدة عند إنشاء رسم بياني للكائن حيث تتواصل الكائنات عبر الرسائل.
من جانب آخر، . public static void main(String[] args) throws IOException {
// Behavior code.
- }
- class Greeter {
- public void greet {
- this.out.println("Hello world!");
- }
- public Greeter(PrintStream out) {
- this.out = out;
- }
- private PrintStream out;
- }
// Construction code.
ومن جهة أخرى، Greeter greeter = new Greeter(System.out); // This may be many lines that connect many objects
وعلى صعيد آخر، greeter.greet; // This is one call to one method on one object in the object graph
علاوةً على ذلك، ستستمر الكائنات التي تم إنشاؤها في main طوال عمر البرنامج. النمط النموذجي هو إنشاء الرسم البياني ثم استدعاء طريقة واحدة على كائن واحد لإرسال تدفق التحكم في الرسم البياني للكائن. تمامًا كما في main أن نقطة الدخول إلى الكود الثابت، فإن هذه الطريقة هي نقطة الدخول إلى الكود غير الثابت للتطبيقات.
import org.springframework.context.ApplicationContext;
ومن ناحية أخرى، import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public static void main(String[] args) {
public class Injector {
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
_Gamepad = new CGamepad;
- // injects dependency on SteamController class through Interface Injection
- _Gamepad.InjectFunctionality(new SteamController);
- break;
- }
- _Gamepad.Showcase;
- }
- }
- class Program
أنواع حقن التبعية
إضافةً إلى ذلك، هناك ثلاث طرق على الأقل يمكن لكائن العميل تلقي مرجع لوحدة خارجية:
private Service service;
- // Set the service that this client is to use.
- @Override
- public void setService(Service service) {
- this.service = service;
- }
- }
public void setService(Service service) {
- // Save the reference to the passed-in service inside this client.
- this.service = service;
- }
CGamepad _Gamepad;
public void SetPlatform(EPlatforms InPlatform)
{
// -- Assembling objects -- //
- BeanFactory beanfactory = new ClassPathXmlApplicationContext("Beans.xml");
- Client client = (Client) beanfactory.getBean("client");
- // -- Using objects -- //
- System.out.println(client.greet);
- }
public class Client {
- // Internal reference to the service used by this client
- private ExampleService service;
- // Constructor
_Platform = InPlatform;
switch(_Platform)
دليل المبتدئين لحقن التبعية
وفي هذا الصدد، حقن التبعية والأشياء القابلة للاختبار: تصميم كائنات متقاربة وقابلة للاختبار - جيريمي ويسكوتن ؛ مجلة دكتور دوب، مايو 2006.
أنماط التصميم: حقن التبعية - مجلة MSDN، سبتمبر 2005
مقالة مارتن فاولر الأصلية التي أدخلت مصطلح حقن التبعية
{
- void InjectFunctionality(IGamepadFunctionality InGamepadFunctionality);
- }
- class CGamepad : IGamepadFunctionalityInjector
- {
- IGamepadFunctionality _GamepadFunctionality;
- public CGamepad
- {
- }
مقارنة التجميع
وفي هذا الصدد، لا تختلف تنفيذات الحاقن المختلفة (المصانع، مواقع الخدمة، وحاويات حقن التبعية فيما يتعلق بحقن التبعية. ما يحدث فرقاً هو المكان الذي يسمح لهم باستخدامه. قم بنقل الاستدعاءات إلى المصنع أو محدد الخدمة خارج العميل وإلى الرئيسية والمفاجئ الرئيسي يجعل حاوية حقن التبعية جيدة إلى حد ما. من خلال نقل كل المعرفة عن الحاقن إلى الخارج، يتم ترك عميل نظيف، خالٍ من المعرفة بالعالم الخارجي. ومع ذلك، يمكن اعتبار أي كائن يستعمل كائنات أخرى عميلاً. الكائن الذي يحتوي على main ليس استثناء. هذا الكائن الرئيسي لا يستعمل حقن التبعية. في الواقع استخدام نمط محدد مواقع الخدمة. لا يمكن تجنب ذلك لأنه يجب أن يتم اختيار تنفذيات الخدمة في مكان ما.
علاوةً على ذلك، لا يؤدي إخارج التبعيات إلى ملفات التكوين إلى تغيير هذه الحقيقة. ما يجعل هذه حقاً جزءًا من التصميم الجيد هو أن محدد الخدمة لا ينتشر في جميع أنحاء قاعدة الكود. يقتصر على مكان واحد لكل تطبيق. هذا يترك بقية الكود الاساسي متاح لاستخدام حقن التبعية لعملاء (نظيفين).
public class Client implements ServiceSetter {
// Internal reference to the service used by this client.
}
// Set the other service to be used by this client
public void setOtherService(Service otherService) {
}
- // Set the other service to be used by this client
- public void setOtherService(Service otherService) {
- this.otherService = otherService;
}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 {
Client(Service service) {
- // Save the reference to the passed-in service inside this client
- this.service = service;
- }
public String GetGamepadName => ControllerName;
ومن جهة أخرى، public void SetVibrationPower(float InPower) => VibratingPower = Math.Clamp(InPower * 100.0f, 0.0f, 100.0f);
}
private void validateState {
// Check the service references of this client
نظرة عامة
إضافةً إلى ذلك، حقن التبعية يفصل إنشاء تبعيات العميل عن سلوك العميل، والذي يسمح لتصاميم البرنامج أن تكون مقترنة بشكل متساهل ومتابعة انعكاس التبعية ومبادئ المسؤولية الواحدة. يتناقض بشكل مباشر مع نمط محدد مواقع الخدمة، والذي يسمح للعملاء بمعرفة النظام الذي يستعملونه للعثور على التبعيات. الحقن، الوحدة الأساسية لحقن التبعية، ليست آلية جديدة أو مخصصة. يعمل بنفس الطريقة التي يعمل بها «تمرير المعلمة».
- كائن (ات) خدمة service لاستخدامها.
- كائن العميل الذي يعتمد على الخدمة (الخدمات) التي يستعملها.
- الواجهات التي تحدد كيفية استخدام العميل للخدمات.
- الحاقن، وهو المسؤول عن إنشاء الخدمات وحقنها في العميل.
- كمثال:
- خدمة - سيارة كهربائية أو غاز أو هجينة أو ديزل.
- العميل - السائق الذي يستعمل السيارة بنفس الطريقة بغض النظر عن المحرك.
- واجهة - تلقائية، تضمن ألا يضطر السائق إلى فهم تفاصيل المحرك مثل التروس.
- حاقن - الوالد الذي اشترى السيارة للطفل وقرر أي نوع.
علاوةً على ذلك، يجب ألا يكون لدى العميل معرفة ملموسة بالتنفيذ المحدد لتبعياته. يجب أن يعرف فقط اسم الواجهة وواجهة برمجة التطبيقات . ونتيجة لذلك، لن يحتاج العميل إلى التغيير حتى إذا تغير ما وراء الواجهة. ومع ذلك، إذا تمت إعادة هيكلة الواجهة من صنف إلى نوع واجهة (أو العكس) فسيحتاج العميل إلى إعادة التجميع هذا مهم إذا تم نشر العميل والخدمات بشكل منفصل. هذا الاقتران المؤسف هو واحد لا يمكن حله بالتبعية. يقدم الحاقن الخدمات إلى العميل. في كثير من الأحيان، يقوم كذلك بإنشاء العميل. قد يربط الحاقن معًا رسمًا بيانيًا معقدًا جدًا للكائن عن طريق معاملة كائن مثل العميل ولاحقًا كخدمة لعميل آخر. قد يكون الحاقن. في الواقع الكثير من الكائنات تعمل معًا ولكن قد لا يكون العميل. قد تتم الإشارة إلى الحاقن بأسماء أخرى مثل: المجمع، الموفر، الحاوية، المصنع، البناء، الربيع، كود البناء، أو الرئيسي.
ومن جهة أخرى، يمكن تطبيق حقن التبعية كنظام، واحد يطلب من جميع الكائنات صنف البناء والسلوك. يمكن أن يؤدي الاعتماد على إطار عمل حقن التبعية بالقيام بالإنشاء إلى حظر استخدام الكلمة المفتاحية "new"، أو بشكل أقل صرامة، السماح بالبناء المباشر لعناصر القيمة فقط.
وفي السياق ذاته، أي كائن يمكن استخدامه يمكن اعتباره خدمة. يمكن اعتبار أي كائن يستعمل كائنات أخرى عميلاً . لا علاقة للأسماء بماهية الكائنات وكل شيء يتعلق بالدور الذي تلعبه الكائنات في أي حقن.
وفي السياق ذاته، بالإشارة إلى «تمرير المعلمة» حيث أن الحقن يحمل ضمناً إضافياً أنه يتم القيام به لعزل العميل عن التفاصيل.
من جانب آخر، إن الحقن يتعلق كذلك بما يتحكم في المرور (وليس العميل) مطلقًا وهو مستقل عن كيفية تحقيق التمرير، سواء عن طريق تمرير مرجع أو قيمة يشمل حقن التبعية أربعة أدوار:
ومن ناحية أخرى، الواجهات هي الأنواع التي يتوقع العميل تبعياتها. المشكلة هي ما يجعلها متاحة. قد تكون بالفعل أنواع من الواجهة يتم تنفيذها بواسطة الخدمات ولكن قد تكون كذلك أصناف مجردة أو حتى الخدمات نفسها على الرغم من أن هذا الأخير قد ينتهك DIP ويضحي بفك الارتباط الديناميكي الذي يتيح الاختبار. يتطلب فقط ألا يعرف العميل هويته وبالتالي لا يتعامل معه على أنه ملموس، مثلًا من خلال إنشائها أو توسيعها.
📝 هل في هذه المقالة خطأ؟
إذا وجدت معلومة تحتاج تصحيحاً أو إضافة، أو أردت حذف المقالة، أرسل لنا طلباً وسنراجعه.
ما رأيك في هذه المقالة؟