العربية

دليل شامل لمبادئ حقن التبعية (DI) وعكس التحكم (IoC). تعلم كيفية بناء تطبيقات قابلة للصيانة والاختبار والتوسع.

حقن التبعية: إتقان عكس التحكم لتطبيقات قوية

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

فهم عكس التحكم (IoC)

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

تدفق التحكم التقليدي

في البرمجة التقليدية، يقوم الصنف عادةً بإنشاء تبعياته الخاصة مباشرة. على سبيل المثال:


class ProductService {
  private $database;

  public function __construct() {
    $this->database = new DatabaseConnection("localhost", "username", "password");
  }

  public function getProduct(int $id) {
    return $this->database->query("SELECT * FROM products WHERE id = " . $id);
  }
}

يؤدي هذا النهج إلى اقتران وثيق بين ProductService و DatabaseConnection. يكون ProductService مسؤولاً عن إنشاء وإدارة DatabaseConnection، مما يجعل اختباره وإعادة استخدامه أمرًا صعبًا.

تدفق التحكم المعكوس مع IoC

مع IoC، يتلقى ProductService كائن DatabaseConnection كتبعية:


class ProductService {
  private $database;

  public function __construct(DatabaseConnection $database) {
    $this->database = $database;
  }

  public function getProduct(int $id) {
    return $this->database->query("SELECT * FROM products WHERE id = " . $id);
  }
}

الآن، لا يقوم ProductService بإنشاء DatabaseConnection بنفسه. بل يعتمد على كيان خارجي لتوفير التبعية. هذا الانعكاس في التحكم يجعل ProductService أكثر مرونة وقابلية للاختبار.

حقن التبعية (DI): تطبيق مبدأ عكس التحكم

حقن التبعية (DI) هو نمط تصميم يطبق مبدأ عكس التحكم. وهو يتضمن توفير تبعيات الكائن للكائن نفسه بدلاً من أن يقوم الكائن بإنشائها أو تحديد موقعها. هناك ثلاثة أنواع رئيسية من حقن التبعية:

الحقن عبر المُنشئ

الحقن عبر المُنشئ هو النوع الأكثر شيوعًا والموصى به من حقن التبعية. يضمن أن يتلقى الكائن جميع تبعياته المطلوبة في وقت الإنشاء.


class UserService {
  private $userRepository;

  public function __construct(UserRepository $userRepository) {
    $this->userRepository = $userRepository;
  }

  public function getUser(int $id) {
    return $this->userRepository->find($id);
  }
}

// Example usage:
$userRepository = new UserRepository(new DatabaseConnection());
$userService = new UserService($userRepository);
$user = $userService->getUser(123);

في هذا المثال، يتلقى UserService نسخة من UserRepository من خلال مُنشئه. هذا يسهل اختبار UserService عن طريق توفير UserRepository وهمي (mock).

الحقن عبر دوال الضبط

يسمح الحقن عبر دوال الضبط بحقن التبعيات بعد إنشاء الكائن.


class OrderService {
  private $paymentGateway;

  public function setPaymentGateway(PaymentGateway $paymentGateway) {
    $this->paymentGateway = $paymentGateway;
  }

  public function processOrder(Order $order) {
    $this->paymentGateway->processPayment($order->getTotal());
    // ...
  }
}

// Example usage:
$orderService = new OrderService();
$orderService->setPaymentGateway(new PayPalGateway());
$orderService->processOrder($order);

يمكن أن يكون الحقن عبر دوال الضبط مفيدًا عندما تكون التبعية اختيارية أو يمكن تغييرها في وقت التشغيل. ومع ذلك، يمكن أن يجعل تبعيات الكائن أقل وضوحًا.

الحقن عبر الواجهة

يتضمن الحقن عبر الواجهة تحديد واجهة تحدد طريقة حقن التبعية.


interface Injectable {
  public function setDependency(Dependency $dependency);
}

class ReportGenerator implements Injectable {
  private $dataSource;

  public function setDependency(Dependency $dataSource) {
    $this->dataSource = $dataSource;
  }

  public function generateReport() {
    // Use $this->dataSource to generate the report
  }
}

// Example usage:
$reportGenerator = new ReportGenerator();
$reportGenerator->setDependency(new MySQLDataSource());
$reportGenerator->generateReport();

يمكن أن يكون الحقن عبر الواجهة مفيدًا عندما تريد فرض عقد معين لحقن التبعية. ومع ذلك، يمكن أن يضيف أيضًا تعقيدًا إلى الكود.

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

قد تصبح إدارة التبعيات يدويًا مملة وعرضة للخطأ، خاصة في التطبيقات الكبيرة. حاويات IoC (المعروفة أيضًا باسم حاويات حقن التبعية) هي أطر عمل تعمل على أتمتة عملية إنشاء وحقن التبعيات. توفر موقعًا مركزيًا لتكوين التبعيات وحلها في وقت التشغيل.

فوائد استخدام حاويات IoC

حاويات IoC الشائعة

تتوفر العديد من حاويات IoC للغات برمجة مختلفة. تتضمن بعض الأمثلة الشائعة ما يلي:

مثال باستخدام حاوية IoC في Laravel (PHP)


// Bind an interface to a concrete implementation
use App\Interfaces\PaymentGatewayInterface;
use App\Services\PayPalGateway;

$this->app->bind(PaymentGatewayInterface::class, PayPalGateway::class);

// Resolve the dependency
use App\Http\Controllers\OrderController;

public function store(Request $request, PaymentGatewayInterface $paymentGateway) {
    // $paymentGateway is automatically injected
    $order = new Order($request->all());
    $paymentGateway->processPayment($order->total);
    // ...
}

في هذا المثال، تقوم حاوية IoC في Laravel تلقائيًا بحل تبعية PaymentGatewayInterface في OrderController وتحقن نسخة من PayPalGateway.

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

يقدم اعتماد DI و IoC مزايا عديدة لتطوير البرمجيات:

زيادة قابلية الاختبار

يجعل DI كتابة اختبارات الوحدات أسهل بكثير. من خلال حقن تبعيات وهمية أو بديلة، يمكنك عزل المكون الذي يتم اختباره والتحقق من سلوكه دون الاعتماد على أنظمة أو قواعد بيانات خارجية. هذا أمر حاسم لضمان جودة وموثوقية الكود الخاص بك.

تقليل الاقتران

الاقتران المنخفض هو مبدأ أساسي لتصميم البرمجيات الجيد. يعزز DI الاقتران المنخفض عن طريق تقليل التبعيات بين الكائنات. هذا يجعل الكود أكثر نمطية ومرونة وسهولة في الصيانة. من غير المرجح أن تؤثر التغييرات في مكون واحد على أجزاء أخرى من التطبيق.

تحسين قابلية الصيانة

تكون التطبيقات المبنية باستخدام DI أسهل عمومًا في الصيانة والتعديل. التصميم النمطي والاقتران المنخفض يجعلان من السهل فهم الكود وإجراء تغييرات دون إدخال آثار جانبية غير مقصودة. هذا مهم بشكل خاص للمشاريع طويلة الأمد التي تتطور بمرور الوقت.

تعزيز قابلية إعادة الاستخدام

يشجع DI على إعادة استخدام الكود عن طريق جعل المكونات أكثر استقلالية واحتواءً ذاتيًا. يمكن إعادة استخدام المكونات بسهولة في سياقات مختلفة مع تبعيات مختلفة، مما يقلل من الحاجة إلى تكرار الكود ويحسن الكفاءة العامة لعملية التطوير.

زيادة النمطية (Modularity)

يشجع DI على التصميم النمطي، حيث يتم تقسيم التطبيق إلى مكونات أصغر ومستقلة. هذا يسهل فهم الكود واختباره وتعديله. كما يسمح لفرق مختلفة بالعمل على أجزاء مختلفة من التطبيق في وقت واحد.

تبسيط الإعدادات

توفر حاويات IoC موقعًا مركزيًا لتكوين التبعيات، مما يسهل إدارة التطبيق وصيانته. هذا يقلل من الحاجة إلى التكوين اليدوي ويحسن الاتساق العام للتطبيق.

أفضل الممارسات لحقن التبعية

للاستفادة بفعالية من DI و IoC، ضع في اعتبارك أفضل الممارسات التالية:

الأنماط المضادة الشائعة (Anti-Patterns)

على الرغم من أن حقن التبعية أداة قوية، فمن المهم تجنب الأنماط المضادة الشائعة التي يمكن أن تقوض فوائدها:

حقن التبعية في لغات البرمجة وأطر العمل المختلفة

يتم دعم DI و IoC على نطاق واسع عبر مختلف لغات البرمجة وأطر العمل. فيما يلي بعض الأمثلة:

Java

غالبًا ما يستخدم مطورو Java أطر عمل مثل Spring Framework أو Guice لحقن التبعية.


@Component
public class ProductServiceImpl implements ProductService {

    private final ProductRepository productRepository;

    @Autowired
    public ProductServiceImpl(ProductRepository productRepository) {
        this.productRepository = productRepository;
    }

    // ...
}

C#

يوفر .NET دعمًا مدمجًا لحقن التبعية. يمكنك استخدام حزمة Microsoft.Extensions.DependencyInjection.


public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddTransient();
        services.AddTransient();
    }
}

Python

تقدم Python مكتبات مثل injector و dependency_injector لتنفيذ DI.


from dependency_injector import containers, providers

class Container(containers.DeclarativeContainer):
    database = providers.Singleton(Database, db_url="localhost")
    user_repository = providers.Factory(UserRepository, database=database)
    user_service = providers.Factory(UserService, user_repository=user_repository)

container = Container()
user_service = container.user_service()

JavaScript/TypeScript

تتمتع أطر العمل مثل Angular و NestJS بإمكانيات حقن تبعية مدمجة.


import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root',
})
export class ProductService {
  constructor(private http: HttpClient) {}

  // ...
}

أمثلة واقعية وحالات استخدام

يمكن تطبيق حقن التبعية في مجموعة واسعة من السيناريوهات. فيما يلي بعض الأمثلة من العالم الحقيقي:

الخاتمة

يُعد حقن التبعية وعكس التحكم من مبادئ التصميم الأساسية التي تعزز الاقتران المنخفض، وتحسن قابلية الاختبار، وتعزز قابلية صيانة تطبيقات البرامج. من خلال إتقان هذه التقنيات واستخدام حاويات IoC بفعالية، يمكن للمطورين إنشاء أنظمة أكثر قوة وقابلية للتوسع والتكيف. يعد تبني DI/IoC خطوة حاسمة نحو بناء برامج عالية الجودة تلبي متطلبات التطوير الحديث.

حقن التبعية: إتقان عكس التحكم لتطبيقات قوية | MLOG