利斯科夫替代原则

为什么要使用 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 的实用方法。