为什么我们的项目中需要接口层/抽象类

本文关键字:接口 抽象类 我们 项目 为什么 | 更新日期: 2023-09-27 18:27:41

我们通常在项目中使用抽象函数/接口。为什么真的需要它?为什么我们不能只选择业务逻辑层、数据访问层和表示层

表示层中的功能:

abc();

业务逻辑层中的功能:

 public void abc()
    {
      //Preparing the list
    }

数据访问层功能:

public abstract void abc();

数据访问SQLServer层功能:

 public override void abc()
    {
       //Connection with database
    }

问题是:为什么需要数据访问层

为什么我们的项目中需要接口层/抽象类

imo,理解这一点的最简单方法是对DataLayer的抽象。

您已经设置了从xml文件中检索数据的函数。但总有一天,您的产品会扩展,xml还不足以作为数据存储。因此,您将传递给某个嵌入式数据库:sqlite。但总有一天,您需要在某些企业环境中重用您的库。因此,现在您需要开发对sqlserveroraclewebservice的访问。。。。在所有这些更改中,您不仅需要更改实际访问数据的代码,还需要更改实际使用数据的代码。那么,那些已经使用多年的代码呢?您在客户端上的第一次xml数据访问并对此感到满意?反向兼容性如何?

拥有抽象(如果不是直接的话)可以解决大多数问题,但肯定会使您的应用程序具有可扩展性,并对更改更具抵抗力,在我们的世界中,这种情况有时发生得太频繁了。

通常,如果您在代码中使用接口,那么您将以依赖注入的形式获得代码的可操作性。

这将帮助您在某些情况下替换部分实现,例如在单元测试期间提供Mock对象。

抽象类或接口实际上不是一个单独的层,它应该是业务逻辑层的一部分,它定义了实际数据访问层(例如SQL数据存储库)需要实现的接口,以向业务层提供数据访问服务。

如果没有这个接口,您的业务层将直接依赖于SQL层,而接口则消除了这种依赖:您将抽象类或接口放入业务逻辑层。然后SQL层(例如,一个单独的程序集)实现抽象类/接口。通过这种方式,SQL层依赖于业务层,而不是相反。

其结果是一个具有独立业务层的灵活应用程序,可以与多个数据存储库协同工作——它所需要的只是一个实现业务层定义的接口的层。它实际上不仅仅是关于数据存储库-您的业务层不应该依赖于上下文(asp.net与控制台应用程序与服务等),也不应该依赖用户界面类、与业务应用程序接口的模块等。

为什么接口:您是否曾在c#中使用使用:使用(Form f=new Form()){}

在这里,您将看到您只能使用中的那些类,使用实现IDisposable接口。

两个互不了解的事物只能使用接口进行交互。接口保证"某些"功能肯定已经通过这种类型实现了。

为什么分层:

这样您就可以有单独的dll,让您可以在不同的应用程序中重用。

基本上,所有这些都是为了代码重用和性能提升。

我想你说的是立面层。

它是一个可选层,将简化业务层的功能。让我们想象一下,你有一个ProductManager和CategoryManager,并且你想做一个涉及同时使用这两种产品的特定操作(例如,让我获得所有类别中排名前五的产品),然后你可以使用一个使用ProductManager和CategoryManager的facade层。

它的灵感来源于Facade Pattern。

抽象有助于创建功能,无论是通过基类、接口还是组合,如果使用得当,都会为代码的维护、可读性和可重用性带来奇迹。

关于问题中发布的代码,标记为"数据访问层"的代码充当业务层使用的公共抽象。通过这样做,DAL的具体实现(例如示例中"数据访问SQLServer层"下的内容)和业务层解耦。现在,您可以实现访问不同数据库的DAL,或者自动提供数据进行测试等。

存储库模式是DAL中工作的一个极好的例子(示例被简化):

public interface IProductRepository
{
    Product Get(int id);
    ...
}
public class SqlProductRepository : IProductRepository
{
    public Product Get(int id) { ... }
    ...
}
public class MockProductRepository : IProductRepository
{
    private IDictionary<int, Product> _products = new Dictionary<int, Product>()
    {
        { 1, new Product() { Name = "MyItem" } }
    };
    public Product Get(int id) { return _products[id]; }
    ...
}
public class AwesomeBusinessLogic
{
    private IProductRepository _repository;
    public AwesomeBusinessLogic(IProductRepository repository)
    {
        _repository = repository;
    }
    public Product GetOneProduct()
    {
        return _repository.GetProduct(1);
    }
}

尽管本例使用接口,但基类的使用也是如此。美妙之处在于,现在我可以将SqlProductRepositoryMockProductRepository馈送到AwesomeBusinessLogic中,并且不必更改AwesomeBusinessLogic的任何内容。如果出现另一种情况,所需要的只是IProductRepository的新实现,而AwesomeBusinessLogic仍然会处理它而不做更改,因为它只通过接口访问存储库。

前面的所有答案都可能解释抽象层的需求,但我仍然想补充一些我的想法。

假设在我们的项目中,每层只有一个服务实现。例如,我有一个联系DAL和一个联系BLL服务,我们可以做一些类似的事情

namespace Stackoverflow
{
    public class ContactDbService
    {
        public Contact GetContactByID(Guid contactID)
        {
            //Fetch a contact from DB
        }
    }
}

联系BLL服务:

namespace Stackoverflow
{
    public class ContactBLLService
    {
        private ContactDbService _dbService;
        public ContactBLLService()
        {
            _dbService = new ContactDbService();
        }
        public bool CheckValidContact(Guid contactID)
        {
            var contact = _dbService.GetContactByID(contactID);
            return contact.Age > 50;
        }
    }
}

而不定义接口/抽象类。

如果我们真的这样做,就会有一些明显的缺点。

  1. 代码通信:想象一下,如果您的项目涉及,您的服务可能有许多不同的方法,那么维护人员(除了您)怎么可能知道您的服务是做什么的?他必须阅读你的整个服务才能修复像InvalidCastOperation这样的小错误吗?通过查看界面,人们将立即了解服务的功能(至少)。

  2. 单元测试

    您可以使用fake/mmock服务测试您的逻辑,以便提前检测错误,并防止稍后发生回归错误。

  3. 更易于更改:

    通过只引用其他类中的接口/抽象类,您可以很容易地在以后替换这些接口实现,而无需付出太多工作。

抽象使您能够快速进行重构。考虑不使用SQL server,而是决定使用其他提供程序;如果您没有数据访问层,那么您需要进行大规模重构,因为您直接调用数据访问方法。但是,如果您有一个数据访问层,那么您只需要编写一个新的数据访问层(从抽象数据访问层继承),并且不会更改业务层中的任何内容。