28 Oct 2023
Examples of Dependency Inversion Principle (DIP):
Example 1: Online Shopping Cart
Problem Statement: The shopping cart system interacts directly with specific product classes, violating the Dependency Inversion Principle. It lacks the necessary abstractions for product management.
// Problem: Shopping cart interacts directly with specific product classes
class ShoppingCart {
private List<Product> cartItems;
public ShoppingCart() {
this.cartItems = new ArrayList<>();
}
// Add a product to the cart
public void addProduct(Product product) {
cartItems.add(product);
}
// Remove a product from the cart
public void removeProduct(Product product) {
cartItems.remove(product);
}
// Display the items in the cart
public void displayCart() {
for (Product product : cartItems) {
System.out.println(product.getName() + ": " + product.getPrice());
}
}
}
Solution Description: Apply the Dependency Inversion Principle by introducing abstractions and interfaces for products. The shopping cart should interact with these interfaces, allowing you to change or extend the product types without modifying the shopping cart logic.
// Solution: Apply DIP by introducing abstractions and interfaces
interface Product {
String getName();
double getPrice();
}
class OnlineProduct implements Product {
private String name;
private double price;
public OnlineProduct(String name, double price) {
this.name = name;
this.price = price;
}
public String getName() {
return name;
}
public double getPrice() {
return price;
}
}
class ShoppingCart {
private List<Product> cartItems;
public ShoppingCart() {
this.cartItems = new ArrayList<>();
}
// Add a product to the cart
public void addProduct(Product product) {
cartItems.add(product);
}
// Remove a product from the cart
public void removeProduct(Product product) {
cartItems.remove(product);
}
// Display the items in the cart
public void displayCart() {
for (Product product : cartItems) {
System.out.println(product.getName() + ": " + product.getPrice());
}
}
}
Example 2: Electrical Appliances and Power Outlets:
Problem Statement: Household electrical appliances directly connect to specific power outlets without using standardized plugs and outlets as abstractions. This approach violates the Dependency Inversion Principle.
// Problem: Traditional power outlets directly connect to specific appliances
class PowerOutlet {
private Appliance appliance;
public PowerOutlet(Appliance appliance) {
this.appliance = appliance;
}
// Plug in and operate the appliance
public void operate() {
appliance.turnOn();
}
}
Solution Description: Apply the Dependency Inversion Principle by introducing standardized plugs and outlets as abstractions. Power outlets should depend on these abstractions, enabling you to easily switch appliances without rewiring the entire building.
// Solution: Apply DIP with standardized plugs and outlets
interface Plug {
void connect();
}
class StandardPlug implements Plug {
public void connect() {
// Connect using standardized plug
}
}
class PowerOutlet {
private Plug plug;
public PowerOutlet(Plug plug) {
this.plug = plug;
}
// Plug in and operate the appliance
public void operate() {
plug.connect();
}
}
Example 3: Financial Trading System:
Problem Statement: The trading strategies directly depend on specific data providers and execution platforms, which contradicts the Dependency Inversion Principle. The absence of abstractions hampers flexibility.
// Problem: Trading strategies depend directly on specific data providers and execution platforms
class TradingStrategy {
private DataProvider dataProvider;
private ExecutionPlatform executionPlatform;
public TradingStrategy() {
this.dataProvider = new DataProvider();
this.executionPlatform = new ExecutionPlatform();
}
// Execute trading strategy using dataProvider and executionPlatform
public void executeStrategy() {
// ...
}
}
Solution Description: Apply the Dependency Inversion Principle by introducing abstractions and interfaces for data providers and execution platforms. Trading strategies should depend on these interfaces, allowing you to swap data providers or execution platforms without rewriting the strategies.
// Solution: Apply DIP by introducing abstractions and interfaces
interface DataProvider {
MarketData fetchMarketData();
}
interface ExecutionPlatform {
void executeTrade(Order order);
}
class TradingStrategy {
private DataProvider dataProvider;
private ExecutionPlatform executionPlatform;
public TradingStrategy(DataProvider dataProvider, ExecutionPlatform executionPlatform) {
this.dataProvider = dataProvider;
this.executionPlatform = executionPlatform;
}
// Execute trading strategy using dataProvider and executionPlatform
public void executeStrategy() {
// ...
}
}
Example 5: Content Management System (CMS):
Problem Statement: The CMS interacts directly with specific storage systems, such as databases, without employing abstractions or interfaces. This direct coupling violates the Dependency Inversion Principle.
// Problem: CMS interacts directly with specific storage systems
class CMS {
private DatabaseStorage database;
public CMS() {
this.database = new DatabaseStorage();
}
// Store content in the database
public void storeContent(Content content) {
database.save(content);
}
}
Solution Description: Apply the Dependency Inversion Principle by introducing an abstract data storage layer. The CMS should interact with this abstraction, enabling you to switch between different storage systems without altering the CMS functionality.
// Solution: Apply DIP by introducing abstractions and interfaces
interface ContentStorage {
void save(Content content);
}
class DatabaseStorage implements ContentStorage {
public void save(Content content) {
// Save content in the database
}
}
class CMS {
private ContentStorage contentStorage;
public CMS(ContentStorage contentStorage) {
this.contentStorage = contentStorage;
}
// Store content using contentStorage
public void storeContent(Content content) {
contentStorage.save(content);
}
}
Example: Video Streaming Service:
Problem Statement: The video streaming platform has a direct dependency between the user interface and low-level components for media codecs and network protocols. This tight coupling violates the Dependency Inversion Principle, hindering adaptability.
// Problem: User interface and streaming components directly depend on specific media playback and network technologies
class VideoPlayer {
private MediaCodec mediaCodec;
private NetworkProtocol networkProtocol;
public VideoPlayer() {
this.mediaCodec = new MediaCodec();
this.networkProtocol = new NetworkProtocol();
}
// Play video content using mediaCodec and networkProtocol
public void playVideo() {
// ...
}
}
Solution Description: Apply the Dependency Inversion Principle by introducing abstractions and interfaces for media codecs and network protocols. The user interface should depend on these abstractions, allowing you to enhance the playback experience or change the underlying network technology without impacting the user interface.
// Solution: Apply DIP by introducing abstractions and interfaces
interface MediaCodec {
void decodeVideoData(byte[] videoData);
}
interface NetworkProtocol {
void establishConnection(String server);
byte[] receiveVideoData();
}
class VideoPlayer {
private MediaCodec mediaCodec;
private NetworkProtocol networkProtocol;
public VideoPlayer(MediaCodec mediaCodec, NetworkProtocol networkProtocol) {
this.mediaCodec = mediaCodec;
this.networkProtocol = networkProtocol;
}
// Play video content using mediaCodec and networkProtocol
public void playVideo() {
// ...
}
}
DIP encourages decoupling high-level modules from low-level modules, allowing for greater flexibility and adaptability in software design.