28 Oct 2023
Examples of Inversion of Control (IoC)
Example 1:
Analogy to help understand IoC:
Imagine a restaurant. The customer does not need to know how to cook the food, or even what ingredients are needed. They simply place an order with the waiter, and the waiter takes care of the rest. The customer is decoupled from the cooking process, and they can focus on enjoying their meal.
In an IoC application, the container or framework is like the waiter. It takes care of creating and managing the dependencies of the objects in the application. The objects themselves simply focus on their core responsibility. This makes the application more loosely coupled and testable.
IoC is a powerful design principle that can be used to improve the quality of software. It is often used in conjunction with other design patterns, such as dependency injection, to create flexible and maintainable applications.
Certainly, here's a simplified pseudo-code example in C# ASP.NET Core to demonstrate Inversion of Control (IoC) and Dependency Injection:
// Define a service interface
public interface IFoodService
{
void ServeFood();
}
// Create a concrete implementation of the service
public class Chef : IFoodService
{
public void ServeFood()
{
// Chef's logic to cook and serve food
Console.WriteLine("Chef: Food is ready!");
}
}
// Create a controller in ASP.NET Core that relies on IFoodService using IoC
[Route("api/restaurant")]
[ApiController]
public class RestaurantController : ControllerBase
{
private readonly IFoodService _foodService;
// IoC: Inject IFoodService into the RestaurantController
public RestaurantController(IFoodService foodService)
{
_foodService = foodService;
}
[HttpGet("place-order")]
public IActionResult PlaceOrder()
{
// Restaurant's logic to take customer's order
Console.WriteLine("Customer: I'd like a meal, please.");
// Let the IFoodService handle cooking and serving
_foodService.ServeFood();
Console.WriteLine("Customer: Enjoying the meal.");
return Ok("Enjoy your meal!");
}
}
// Set up dependency injection in Startup.cs
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
// Register Chef as the implementation of IFoodService
services.AddScoped<IFoodService, Chef>();
}
// Other configuration methods...
}
// Main program
public static void Main(string[] args)
{
var host = CreateHostBuilder(args).Build();
host.Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
In this pseudo-code, we have defined an IFoodService interface, a Chef class that implements it, and a RestaurantController in ASP.NET Core that relies on IFoodService using Inversion of Control. The Chef is registered as the implementation of IFoodService in the Startup class. When a customer places an order by accessing the PlaceOrder endpoint in the RestaurantController, the IoC container injects the appropriate IFoodService implementation (in this case, Chef), demonstrating how IoC helps in making the application more loosely coupled.
Example 2:
Scenario: Starting a Car remotely through an API call
Imagine you are building a modern car management system in an ASP.NET Core WebAPI application. The system allows users to start their cars remotely through an API call. To implement this, you use the IoC and Dependency Injection principles:
-
Car Class: The
Carclass represents a car in the system. It has a complex set of features, including engine management. However, it doesn't directly manage the engine itself. Instead, it relies on anEnginecomponent for starting and stopping the engine. -
Engine Class: The
Engineclass is responsible for managing the car's engine. It has aStart()method that handles the engine start process. -
IoC Container: In this system, the IoC container is represented by ASP.NET Core's built-in dependency injection. The system is configured to know how to create instances of the
CarandEngineclasses and manage their dependencies. -
CarsController: This is an API controller that exposes an endpoint
/api/cars/start. When a user sends a request to this endpoint, theStartCaraction is executed. It injects aCarinstance and calls theStart()method on theCar, which, in turn, starts the engine.
User Interaction:
- A user accesses your car management system's API via a web or mobile application.
- The user sends a POST request to
/api/cars/start, indicating that they want to start their car remotely.
System Interaction:
- The ASP.NET Core application processes the user's request.
- The
CarsControlleris invoked and creates an instance of theCarclass via the IoC container. - The
Carinstance, in turn, relies on theEngineto start the car. - The
Engineclass executes itsStart()method to start the engine.
Outcome:
- The car's engine is successfully started, allowing the user to use their car remotely.
In this scenario, the IoC and Dependency Injection principles help manage the complex relationship between the car and its engine. The system is highly modular and testable, and you can easily replace components or add new features without impacting the rest of the system.
Here's a pseudo code example of how you can implement the given IoC example in a C# ASP.NET Core WebAPI application:
// Car class
public class Car
{
private readonly IEngine engine;
public Car(IEngine engine)
{
this.engine = engine;
}
public void Start()
{
engine.Start();
}
}
// Engine interface
public interface IEngine
{
void Start();
}
// Engine class
public class Engine : IEngine
{
public void Start()
{
// Engine start logic
}
}
// IoC container using built-in ASP.NET Core dependency injection
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
// Register the Engine and Car classes with the IoC container
services.AddTransient<IEngine, Engine>();
services.AddTransient<Car>();
}
}
// Controller to use the Car class
[Route("api/cars")]
[ApiController]
public class CarsController : ControllerBase
{
private readonly Car car;
public CarsController(Car car)
{
this.car = car;
}
[HttpGet("start")]
public IActionResult StartCar()
{
car.Start();
return Ok("Car started");
}
}
// Main program
public static void Main(string[] args)
{
var host = CreateHostBuilder(args).Build();
host.Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
In this example:
-
The
Carclass andEngineclass are defined, but we've introduced anIEngineinterface to make it easier for dependency injection. -
The
IoCContaineris replaced with the built-in dependency injection container provided by ASP.NET Core. In theStartupclass, we configure services and registerEngineandCarwith the IoC container. -
In the
CarsController, we inject theCarclass as a dependency. When the/api/cars/startendpoint is accessed, theStartCaraction is executed, and it calls theStartmethod on the injectedCarinstance, demonstrating how IoC works with ASP.NET Core's built-in dependency injection.