利斯科夫替代原则
为什么要使用 LSP
场景: 假设我们有 3 个数据库(抵押客户,经常账户客户和储蓄账户客户)提供客户数据,我们需要客户的姓氏客户详细信息。现在,根据给定的姓氏,我们可以从这 3 个数据库中获得超过 1 个客户详细信息。
履行
商业模式层:
public class Customer
{
// customer detail properties...
}
数据访问层:
public interface IDataAccess
{
Customer GetDetails(string lastName);
}
上面的接口由抽象类实现
public abstract class BaseDataAccess : IDataAccess
{
/// <summary> Enterprise library data block Database object. </summary>
public Database Database;
public Customer GetDetails(string lastName)
{
// use the database object to call the stored procedure to retirve the customer detials
}
}
这个抽象类为所有 3 个数据库都有一个通用方法 GetDetails
,它由每个数据库类扩展,如下所示
抵押客户数据访问:
public class MortgageCustomerDataAccess : BaseDataAccess
{
public MortgageCustomerDataAccess(IDatabaseFactory factory)
{
this.Database = factory.GetMortgageCustomerDatabase();
}
}
当前账户客户数据访问:
public class CurrentAccountCustomerDataAccess : BaseDataAccess
{
public CurrentAccountCustomerDataAccess(IDatabaseFactory factory)
{
this.Database = factory.GetCurrentAccountCustomerDatabase();
}
}
节省账户客户数据访问:
public class SavingsAccountCustomerDataAccess : BaseDataAccess
{
public SavingsAccountCustomerDataAccess(IDatabaseFactory factory)
{
this.Database = factory.GetSavingsAccountCustomerDatabase();
}
}
一旦设置了这 3 个数据访问类,现在我们将注意力吸引到客户端。在 Business 层中,我们有 CustomerServiceManager 类,它将客户 detials 返回给客户端。
业务层:
public class CustomerServiceManager : ICustomerServiceManager, BaseServiceManager
{
public IEnumerable<Customer> GetCustomerDetails(string lastName)
{
IEnumerable<IDataAccess> dataAccess = new List<IDataAccess>()
{
new MortgageCustomerDataAccess(new DatabaseFactory()),
new CurrentAccountCustomerDataAccess(new DatabaseFactory()),
new SavingsAccountCustomerDataAccess(new DatabaseFactory())
};
IList<Customer> customers = new List<Customer>();
foreach (IDataAccess nextDataAccess in dataAccess)
{
Customer customerDetail = nextDataAccess.GetDetails(lastName);
customers.Add(customerDetail);
}
return customers;
}
}
我没有展示依赖注入以保持简单,因为它现在已经变得复杂了。
现在,如果我们有一个新的客户详细数据库,我们可以添加一个扩展 BaseDataAccess 并提供其数据库对象的新类。
当然,我们在所有参与的数据库中需要相同的存储过程
最后,CustomerServiceManagerclass 的客户端将只调用 GetCustomerDetails 方法,传递 lastName,而不应关心数据的来源和位置。
希望这能为你提供一种理解 LSP 的实用方法。