依赖倒置原则(Dependency Inversion Principle,DIP)是面向对象设计的一个重要原则,是SOLID五大设计原则之一。它旨在减少模块之间的依赖性,使得代码更加灵活、可扩展、可维护。依赖倒置原则有两个核心思想:
高层模块不应该依赖低层模块,二者都应该依赖于抽象。
抽象不应该依赖于细节,细节应该依赖于抽象。
假设我们有一个日志记录模块(Logger)和一个邮件通知模块(EmailNotifier)。传统设计可能是这样:
public class Logger {
public void Log(string message) {
// 写入日志文件
}
}
public class EmailNotifier {
private Logger logger = new Logger(); // 直接依赖于具体实现类
public void SendEmail(string email, string message) {
// 发送邮件
logger.Log("Email sent to " + email);
}
}
在这个例子中,EmailNotifier
依赖于 Logger
的具体实现。如果以后想换成数据库记录日志,而不是写入文件,就需要修改 EmailNotifier
的代码,这就导致了高层模块对低层模块的紧耦合。
为了遵循依赖倒置原则,可以这样改造:
public interface ILogger {
void Log(string message);
}
public class FileLogger : ILogger {
public void Log(string message) {
// 写入日志文件
}
}
public class EmailNotifier {
private ILogger logger;
public EmailNotifier(ILogger logger) {
this.logger = logger;
}
public void SendEmail(string email, string message) {
// 发送邮件
logger.Log("Email sent to " + email);
}
}
在这个改进后的设计中,EmailNotifier
依赖于抽象的 ILogger
接口,而不是具体的 FileLogger
实现类。通过这种方式,我们可以轻松替换 FileLogger
为其他日志记录实现,而无需修改 EmailNotifier
的代码。这就遵循了依赖倒置原则,使代码更具灵活性和可扩展性。
假设我们有一个购物系统,支持不同的支付方式,如信用卡支付和 PayPal 支付。传统设计可能直接依赖于具体的支付类:
public class CreditCardPayment {
public void ProcessPayment(decimal amount) {
// 处理信用卡支付
}
}
public class PayPalPayment {
public void ProcessPayment(decimal amount) {
// 处理 PayPal 支付
}
}
public class ShoppingCart {
private CreditCardPayment creditCardPayment = new CreditCardPayment();
public void Checkout(decimal amount) {
creditCardPayment.ProcessPayment(amount);
}
}
在这个例子中,ShoppingCart
类直接依赖于 CreditCardPayment
类,如果我们想换成 PayPal 支付,就需要修改 ShoppingCart
类的代码。
使用依赖倒置原则,我们可以引入一个抽象的支付接口,然后在 ShoppingCart
中依赖这个接口,而不是具体的支付类:
public interface IPaymentProcessor {
void ProcessPayment(decimal amount);
}
public class CreditCardPayment : IPaymentProcessor {
public void ProcessPayment(decimal amount) {
// 处理信用卡支付
}
}
public class PayPalPayment : IPaymentProcessor {
public void ProcessPayment(decimal amount) {
// 处理 PayPal 支付
}
}
public class ShoppingCart {
private IPaymentProcessor paymentProcessor;
public ShoppingCart(IPaymentProcessor paymentProcessor) {
this.paymentProcessor = paymentProcessor;
}
public void Checkout(decimal amount) {
paymentProcessor.ProcessPayment(amount);
}
}
这样一来,无论是使用信用卡支付还是 PayPal 支付,ShoppingCart
类都不需要修改,只需传入不同的支付处理实现即可。
假设我们有一个应用程序,需要将数据保存到不同的存储系统中,如文件或数据库。传统设计可能是直接依赖于具体的存储类:
public class FileStorage {
public void SaveData(string data) {
// 保存数据到文件
}
}
public class DataHandler {
private FileStorage storage = new FileStorage();
public void HandleData(string data) {
storage.SaveData(data);
}
}
在这个例子中,DataHandler
直接依赖于 FileStorage
类。如果我们想换成数据库存储,就需要修改 DataHandler
的代码。
通过依赖倒置原则,可以这样设计:
public interface IStorage {
void SaveData(string data);
}
public class FileStorage : IStorage {
public void SaveData(string data) {
// 保存数据到文件
}
}
public class DatabaseStorage : IStorage {
public void SaveData(string data) {
// 保存数据到数据库
}
}
public class DataHandler {
private IStorage storage;
public DataHandler(IStorage storage) {
this.storage = storage;
}
public void HandleData(string data) {
storage.SaveData(data);
}
}
现在,DataHandler
不再依赖于具体的存储实现,而是依赖于一个抽象的 IStorage
接口。我们可以在运行时选择不同的存储方式,而不需要修改 DataHandler
的代码。
你学废了麽?