【问题标题】:Is it good design to have state-representative properties on a service class?在服务类上具有状态代表属性是一种好的设计吗?
【发布时间】:2021-03-26 17:04:12
【问题描述】:

我有一个带有服务类的应用程序,它从数据库中检索数据库元数据并将其返回给调用类。

要连接到数据库,服务类方法接受一个详细说明连接凭据的参数。

我正在考虑将凭据存储在服务类中的更改。造成这种情况的原因之一是调用类(例如,负责比较不同服务器上的模式)可能连接到多个不同的数据库/服务器,因此调用类基本上会有这些服务类的集合,而不是而不是连接凭据的集合(例如,以下示例中的 IConnectionInfo)。

我可能想在应用程序中做的另一件事是为不同类型的 RDBMS(例如 SQL Server、Oracle 等)实现此服务类(以下示例中为 IDatabaseService),这似乎就像让它保持开放的最佳方式(从服务返回的信息将非常通用,适用于所有受支持的 RDBMS 类型)。

服务类的示例代码:

public class DatabaseService : IDatabaseService
{
    private readonly IConnectionInfo ConnectionInfo;
    
    public bool IsConnected; // INotifyPropertyChanged
    
    public string ServerName => IConnectionInfo.ServerName;
    public string DatabaseName => IConnectionInfo.DatabaseName;
    
    public DatabaseService(IConnectionInfo connectionInfo)
    {
        ConnectionInfo = connectionInfo;
    }
    
    public IEnumerable<Table> GetTables()
    {
        ...
    }
    
    public IEnumerable<Column> GetTableColumns(Table table)
    {
        ...
    }
}

我对这种方法有点怀疑的原因有几个:

  1. 我正在这个类中实现INotifyPropertyChanged,以便我可以更新 UI 以向用户显示他们是否已连接(例如,如果对服务器的任何调用失败,我可以将其切换为 false)。出于这个原因,它似乎表现得像一个 ViewModel(而不是一个服务)。

  2. 我不确定在服务类上具有表示其状态的属性是否是一种好习惯,例如ConnectionInfoIsConnected

上面的设计看起来是可以接受的吗?

更新:进一步的思考和解释

我想我要解决的特定编程问题是,例如,我可能有一个用于 SQL Server 凭据的类和一个用于 Oracle 凭据的类,两者都是IConnectionCredentials。然后我会有几个对应的IDataService 实现,它们接受IConnectionCredentials 作为参数。问题是并非所有IDataService 的实现都可以与IConnectionCredentials 的所有实现一起工作,这对我来说似乎有缺陷,所以我认为将数据访问层和“数据访问器”对象组合到一节课。我想让IDataService 包含确定要使用哪个版本的“真实”数据访问接口的逻辑可能是可行的。例如:

public class DataService : IDataService
{
    private readonly RealDataServiceFactory RealDataServiceFactory;
    
    public IEnumerable<Table> GetTables(IConnectionCredentials connectionCredentials)
    {
        return RealDataServiceFactory.Create(connectionCredentials).GetTables(connectionCredentials);
    }
}

public class RealDataServiceFactory
{
    public IRealDataService Create(IConnectionCredentials connectionCredentials)
    {
        if (connectionCredentials is SqlServerConnectionCredentials)
        {
            return new SqlServerDataService();
        }
        else if ...
    }
}

我想要在数据访问类中使用IsConnected 属性的另一个原因是,除了连接不工作之外,服务可能不返回数据还有其他原因,而且我没有感觉到确定属于的逻辑调用类,因此喜欢数据服务可以同时返回 null 到某个调用以及向应用程序和 UI 状态的想法,“我的连接有问题”。在上面的实现中,我也会失去这个,虽然我想这可以通过在返回之前将传入的IConnectionCredentials 上的数据服务标记为IsConnected 来实现。

【问题讨论】:

  • DatabaseService 本身只是一个模型。是否使用 INotifyPropertyChanged 属性将其公开给视图不会影响 MVVM 设计。恕我直言,MVVM 只是一种“关注点分离”的方法,它曾经被称为三层架构……它只是旧设计模式的一个新名称。
  • @ΩmegaMan 谢谢 - 提供更多关于该类将要做什么的信息,例如,它可能有一个使用SqlConnection 连接到数据库并检索数据的方法,等等 - 它仍然会被归类为简单的模型吗?
  • 您在处理连接的模型(和实例)上有业务逻辑SqlConnection操作/实例。该主实例将驻留在 VM 上,视图将绑定到该实例的属性以显示状态。如何拉动连接的杠杆,由您决定。但在过去的 20 年里,我已经完成了您提出的类似设计。 MVVM 纯粹主义者可能会抱怨,但只要您没有在此类上暴露给 View 的 SQL 注入场景,这才是最重要的,并且此类(模型)可以在其他地方重用。这是我的看法,我可能是错的。
  • The Spiffing Brit 会称其为“公平和平衡”。 :-)
  • @ΩmegaMan 再次感谢,这对我来说绝对有意义。作为最后一个问题:在反思这一点时,我记得我在当前的 DatabaseService 上有一个方法,它接受两个 ConnectionInfo 参数,因此它可以进行比较。使用这种新型设计,在DatabaseService 上设置一个接受另一个DatabaseService 作为参数的方法是否有意义/可以接受?

标签: c# wpf design-patterns mvvm


【解决方案1】:

这最终取决于您要做什么,但听起来这种设计将两个(或更多?)关注点合二为一:

  • 用户界面更新 (INotifyPropertyChanged)
  • 数据访问

这给了班级不止一个改变的理由。换句话说,它违反了Single Responsibility Principle (SRP)。

现在,没有人说您必须遵守 SRP。这和其他SOLID principles 是处理某些复杂性的指南。如果您没有 SOLID 解决的问题,那么您不必遵循这些原则。

但在实践中,很难预测未来的问题。代码库从一开始就很少有问题。它慢慢地从简单的事情转向更复杂的事情。

虽然提议的设计听起来像是混合了关注点(而不是关注点分离),但实际上它可能是良性的。毕竟,INotifyPropertyChanged 是一个基类库接口,所以您没有引入与某些特定技术的耦合。不过,我会警惕扩大该类上与 UI 相关的更新范围。

【讨论】:

  • 感谢您的回答,它确实总结了我的想法。虽然我怀疑我是否会扩展课程中与 UI 相关的元素,但它仍然不适合我。我已经用一些进一步的信息更新了我的问题,这些信息更具体地解释了我要解决的编程问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-04-26
  • 2012-02-24
  • 1970-01-01
  • 2011-02-16
  • 1970-01-01
相关资源
最近更新 更多