What is interface injection?
Interface injection is a dependency injection technique that is a design pattern widely used in object-oriented programming that promotes loose component coupling in a software system. Interface injection emphasizes injecting dependency through interfaces instead of concrete implementation, with which we can achieve flexibility and maintainability in software designs.
This Answer will delve deep into the fundamentals of interface injection, its benefits, and practical coding examples to better understand its usage.
Interface injection
Interface injection focuses on using interfaces as contracts between different components. An interface contains a set of method definitions a class must implement. Classes can work with any function implementation that complies with the defined contracts by interface injection, which enables components to be swapped out without changing the existing codebase.
Implementing interface injection
Here’s an example in TypeScript to illustrate interface injection, which supports interfaces:
interface Shape {area(): number;}// Class that depends on the Shape interface (Interface Injection)class CreateShape {private shape: Shape;constructor(shape: Shape) {this.shape = shape;}calculateArea() {return this.shape.area();}}class Circle implements Shape {private radius: number;constructor(radius: number) {this.radius = radius;}area() {return Math.PI * this.radius * this.radius;}}class Square implements Shape {private side: number;constructor(side: number) {this.side = side;}area() {return this.side * this.side;}}const circle = new Circle(5);const square = new Square(4);const circleShape = new CreateShape(circle);const squareShape = new CreateShape(square);console.log("Circle Area:", circleShape.calculateArea()); // Output: Circle Area: 78.53981633974483console.log("Square Area:", squareShape.calculateArea()); // Output: Square Area: 16
Lines 1–3: We define an interface named
Shape. An interface defines a contract for the classes to follow. In this case, any class implementing theShapeinterface must have a methodareathat returns anumber.Lines 5–17: The
CreateShapeclass takes a dependency on theShapeinterface through its constructor. This means it can hold any object that implements theShapeinterface. This is where interface injection occurs; an external object that adheres to theShapeinterface is injected into theCreateShapeclass.Line 7: We declare a
shapeprivate property of theShapetype. This property will hold an object that implements theShapeinterface.Line 11: The constructor assigns the argument of
Shapetype to theshapeproperty of theCreateShapeclass.Lines 14–16: The
calculateAreamethod calls theareamethod of the object stored in theshapeproperty without knowing the specific implementation details.
Lines 20–30: The
Circleclass implements theShapeinterface and itsareafunction is implementing the method required by theShapejwhich calculates the area of the circle with the given radius.Lines 32–42: The
Squareclass implements theShapeinterface. Itsareafunction implements the method required by theShapeinterface, which calculates the area of the square with the given dimension.Lines 44–45: We create the instances of the
CircleandSquareclasses respectively.Line 47: Here, we create the class instance of
CreateShapeand inject the circle object into its contructor. Now, thecircleShapecalculates the area of the circle.Line 49: We create the class instance of
CreateShapeand inject thesquareobject in its constructor, which ultimately calculates the area of the square.Lines 51–52: We call the instances of
CreateShape, which in turn calls theareamethod of the injected objects further logged to the console.
Benefits of interface injection
Here are a few benefits of using interface injection:
Flexibility and extensibility: It promotes flexibility by allowing easy substitution of implementations. Extensibility and scalability are ensured by adding new implementations without modifying existing code.
Testability: Interfaces make testing a simple and easy process by allowing us to use mock objects. Full unit testing can be done by injecting interfaces using mock implementations without involving their concrete implementations.
Reduced coupling: By using interfaces, coupling between components can be reduced, and using such components results in a more modular and maintainable codebase because these components rely on abstractions (interfaces) instead of their concrete implementations.
Wrap up
Interface injection is a potent technique in the realm of dependency injection. Using interfaces between components as contracts promotes flexibility, testability, and reduced coupling in object-oriented systems. We can create more maintainable, modular, and testable applications by incorporating interface injection into our software design practices. It also ensures the robustness of the codebase in case of changing requirements and future expansions.
Free Resources