【问题标题】:Interface-oriented: Fragmentized interfaces to represent a data structure面向接口:将接口分段来表示数据结构
【发布时间】:2025-12-06 03:05:02
【问题描述】:

我的灵感来自于接口实现的封装。当对象共享公共属性和方法时,接口是一个强大的工具。面向接口的设计是将实体和管理它们的管理器分离的设计。他们不需要知道任何实现细节和确切的类型,典型的良好接口设计是通过方法调用的对象可以在运行时热插拔。

所以我设法将我的对象通过接口公开。

假设我有一个LoginModule,其中包含处理Client 提交的Form 的业务逻辑。例如,客户端可以通过输入UsernamePassword 或使用他们的MobileNo 登录

public class UserLoginForm
{
  public string Username { get; set; }
  public string Password { get; set; }
}
public class MobileLoginForm
{
  public string MobileNo { get; set; }
  public string Password { get; set; }
}

然后业务逻辑层去看看请求。

public class LoginModule
{
  public int LoginByUsername(UserLoginForm form)
  {
    // DoSomething, such as CheckDbIfExist(form.Username);
    // return the result, e.g. -1 stand for wrong password
  }
   public int LoginByMobileNo(MobileLoginForm form)
  {
    // DoSomething, such as CheckDbIfExist(form.Username);
    // return the result, e.g. -1 stand for wrong password
  }
}

在这个例子中,两个逻辑非常相似,以至于LoginByMobileNo最终可以通过在数据库中查找MobileNo得到Username后直接调用LoginByUserName

但你知道,它不能,因为我们需要一个UserLoginForm 来调用它,或者我们只是创建一个单独的方法Login(string username, string password) 让两个登录方法都调用它。

实际考虑的不是如何重构这两种方法,因为还有许多其他类型的请求,例如CokeRequest 请求将可乐运送到int RoomNoint Count... ...

public interface ILoginModule
{
   void LoginByUsername(XXX xxx);
}

第一个问题:XXX 是什么?

这个接口无权访问UserLoginForm,反正他也不知道那个表单的存在。如果我做的界面设计正确,XXX应该是一个他可以知道它存在的界面。

public interface ILoginModule
{
   void LoginByUsername(IUserLogin login);
   void LoginByMobile(IMobileLogin login);
}
// LoginModule now implement ILoginModule

public interface IUserLogin
{
   string UserName { get; set; }
   string Password { get; set; }
}
public interface IMobileLogin
{
   string MobileNo { get; set; }
   string Password { get; set; }
}
// The the two Forms now implement the respective interface

到那时,噩梦开始了。我发现客户提出的每个请求都需要一个专用接口

一个有趣的观察:

服务接口是纯方法接口

请求接口是纯属性接口

我不认为这是处理接口的正确方法。

如果我们将接口碎片化,也不会。

public interface ILoginModule
{
   void LoginByUsername(IUserLogin login);
   void LoginByMobile(IMobileLogin login);
}
// LoginModule now implement ILoginModule

public interface IPassword
{
   string Password { get; set; }
}
public interface IUserLogin : IPassword
{
   string UserName { get; set; }
}
public interface IMobileLogin : IPassword
{
   string MobileNo { get; set; }
}
// The the two Forms now implement the respective interface

因为我们仍然需要数百个接口(与请求的种类相同) 也没有

public interface ILoginModule
{
   void LoginByUsername(IUser login1, IPassword login2);
   void LoginByMobile(IMobile login, IPassword login2);
}
// LoginModule now implement ILoginModule

public interface IPassword
{
   string Password { get; set; }
}
public interface IUser
{
   string UserName { get; set; }
}
public interface IMobile
{
   string MobileNo { get; set; }
}
// The the two Forms now implement the respective interface

它闻起来很糟糕,因为我们与一个一个传递字符串内容没有什么不同。如果请求包含8个参数,则方法变为DoWork(form,form,form,form,form,form,form,form)

我真的不明白为什么我会陷入这种讽刺。如果我有n种请求,把它们交给m个模块来处理是正常的,总共还是n个方法。如果我们使用接口,它会添加 n 个接口来表示 n 个请求,这对随时热插拔数据没有帮助,因为每个接口只有一个实现

在这种情况下,LoginModule 甚至无法从中受益:在任何情况下,它都无法在找到对应于MobileNoUsername 后调用LoginByUsername...

大多数接口教程都在使用 carbusracecar,其中 void StartEngine()...虽然很少用于架构或层之间的通信,例如 MVC 模型。

我并不是说接口不好,但我需要一种方法来正确实现它,或者我应该在这种情况下实现它。毫无疑问,任何滥用的模式都是反模式。 我希望听到任何声音。

【问题讨论】:

    标签: c# design-patterns interface


    【解决方案1】:

    经过几个小时的考虑,发现起点有问题,导致解决方案有问题。

    使用接口表示数据结构的答案是。声明纯属性接口是没有用的。

    当涉及到业务逻辑时,客户端提交的请求乏善可陈,只需获取其属性并完成工作。

    也就是说,定义void LoginWithMobile(string MobileNo, string Password)ILoginModule 已经足以处理请求,而无需将Request 本身作为参数传递。

    接口可用于抽象“它做什么”而不是 “它包含什么”。因此所有旨在存储数据的对象 没有自己管理它们不应该实现任何接口。

    【讨论】: