用.Net中的接口实现标准化

本文关键字:实现 标准化 接口 Net | 更新日期: 2023-09-27 18:24:54

我有一个关于OOP和接口的奇怪问题,在试图找到最佳设计时,这个问题让我头脑混乱。

有两个不同的类在不同的环境中进行相同的工作(例如发送消息)。这两个环境使用不同的参数来定义接收者;一个使用邮件地址,另一个使用用户名。所以这两个类具有相同但略有不同的发送消息的方法。

MessageSenderViaMailManager.cs

public bool SendMessage(string recipientMailAddress, string message) {
..
}

MessageSenderViaUsernameManager.cs

public bool SendMessage(string recipientUserName, string message) {
..
}

在这两个类中还有其他类似的方法,负责相同的工作,但可能需要不同的参数。为了使这些管理器可以通过接口使用,我创建了一个名为IMessageSenderManager的管理器,并包含这样的定义。

public bool SendMessage(string recipientUserName, string recipientMailAddress, string message);

因此,我的类中的两个SendMessage方法都被更改为:

public bool SendMessage(string recipientUserName,string recipientMailAddress, string message) {
 ..
}

使用这个新的SendMessage方法,我可以使用适当的参数作为收件人(邮件地址或用户名)。这看起来还可以,但暗示看起来很奇怪。因为我必须发送所有参数,而不知道在编码时会在运行时使用哪些参数。例如:

// Sending message via username implementation
string userName = GetUserNameFromSomeWhere();
string mailAddress = GetUserMailFromSomeWhere();
IMessageSenderManager manager = MessageSenderFactory();
manager.SendManager(userName, mailAddress, "This messaged sent by your user name");

和上面的代码一样,通过邮件地址发送消息看起来很相似。

在我看来,这不是一个好的设计,所以我开始考虑一个更好的解决方案。因为如果我想实现另一个MessageSender提供程序,它为接收方使用不同的描述符,我必须向我的接口添加另一个参数,也就是向所有类添加参数。我想,我可以用一个通用收件人参数更改两个收件人参数,并为上下文发送适当的值。但我正在尝试在动态环境中使用它,并且方式(通过用户名或邮件)将在运行时确定,所以我不能使用它。

我计划制作一个灵活的库,它可以解耦使用,也可以对其他开发人员友好地进行单元测试,我不想把它们与无意义的参数或糟糕的设计混淆。

对于这种情况,有什么更好的设计吗?

编辑:

事实上,由于我的错误,我忘记了问题中非常重要的部分,对此我感到抱歉。正如你从答案中看到的,有一些替代方案可以解决我上面描述的第一个问题。但是呢?我在代码中提到,接口是从MessageSenderFactory()方法返回的,但我不知道返回了哪个消息发送方管理器。所以我有两个选择,

  1. 编写if条件以检查从方法返回的管理器,并为该管理器发送具有正确值的强制参数,并将其他参数发送为空
  2. 不管管理器是谁,都要发送具有正确值的所有参数,这样两个参数都可以正常工作。但在未来,如果添加了另一个管理器,我每次都需要为该管理器发送额外的参数
  3. 还有别的办法我还想不出来吗

此外,SendMessage以外的其他方法可能根据管理器需要不同的参数,这在运行时是未知的。例如:

MessageSenderViaMailManagerAddContact方法可能需要以下参数:

  • 联系人姓名
  • 联系人邮件
  • 联系人电话号码
  • 联系人邮件类型(rich,plain)

MessageSenderViaUserNameManagerAddContact方法可能需要以下参数:

  • 联系人姓名
  • 联系人邮件
  • 联系人用户名
  • 联系信息平台(Twitter,facebook,vs)
  • 联系人发件人姓名

所以这使得一切都变得非常复杂。我的IMessageSenderManger的AddMethod应该如何?它应该包含所有参数吗?我应该超载吗?或者我应该在方法中放入通用参数,并使其他因管理器而异的参数匿名(如MVC中的HtmlHelper)

我知道这个问题不是很扎实,而且我不擅长用英语描述。

GitHub编辑:

我创建了一个小例子并上传到github,我希望这能帮助我更好地解释我的问题https://github.com/bahadirarslan/InterfaceDesign

用.Net中的接口实现标准化

更新: 为了清晰起见,对以前的答案进行了编辑

我已经更改了代码以利用您的结构。

public static class MessageSenderManagerFactory
{
  public static IMessageSenderManager Create(IRecipient recipient)
  {
    return new MessageSenderManager { Recipient = recipient };
  }
}
public interface IMessageSenderManager
{
  public IRecipient Recipient { get; set; }
  bool SendMessage(string message);
}
public class MessageSenderManager : IMessageSenderManager
{
  public IRecipient Recipient { get; set; }
  public bool SendMessage(string message)
  {
    // At this point you construct the actual message and sending mechanism.
    // You'll have all the information you need in the TheUser property of Recipient.
    // The following is an example how this can be implemented but since you have not
    // provided what information you need to send or HOW you send the message I can't
    // be more specific.
    var messageToSend = new Message(message);
    messageToSend.Address = Recipient.GetRecipientAddress();
    messageToSend.Send();
  }
}
public interface IRecipient
{
  public string GetRecipientAddress();
}
public abstract class RecipientBase
{
  public User TheUser { get; set; }
  private RecipientBase() { }
  protected RecipientBase(string userId) { TheUser = FindUserById(userId); }
}
public class MailRecipient : RecipientBase, IRecipient
{
  public MailRecipient(string userId) : base(userId) { }
  public string GetRecipientAddress() { return TheUser.Mail; }
}
public class UserNameRecipient : RecipientBase, IRecipient
{
  public UserNameRecipient(string userId) : base(userId) { }
  public string GetRecipientAddress() { return TheUser.UserName; }
}

因此,当您有用户id时,您将使用一个的以下行,具体取决于收件人的类型(就像您在git示例中使用switch case一样):

var manager = MessageSenderManagerFactory.Create(new MailRecipient(userId));
var manager = MessageSenderManagerFactory.Create(new UserNameRecipient(userId));

使用哪种类型的Recipient的逻辑不应基于用户id。数据库或User对象中应该有一个标志或设置来指定这一点。

然后发送消息:

manager.SendMessage(message);

免责声明:未测试代码

为什么不让你的界面如下:

public bool SendMessage(string recipient, string message) {
..
}

然后,在每个类的逻辑中,您添加一些代码来验证收件人信息,例如,您的实现需要一个电子邮件地址,添加验证来检查您传递的是有效的电子邮件地址。然后,在需要用户名的实现中,验证是否可以找到用户。

您可以始终在工厂中实现该检查,这样它就知道要创建哪个类。

我将创建一个包含接收方属性的class MailMessage。则在该MailMessage类上具有AddRecipient方法的2个重载。每种添加收件人的方式都有一个。

设计总是一组特定需求的结果。OO也没什么不同:实现一个可以在各种情况下发送电子邮件的库的方法几乎是无限的。哪种设计/实现是好的完全取决于您的情况。因此,除非你透露更多关于你的需求的细节,否则询问"什么设计是最好的"是没有意义的:

为什么您希望/需要重新设计您已经实施的内容

尽管如此,我同意您选择的抽象(一种具有多个参数的方法,根据客户拥有的信息,这些参数是可选的或互斥的)似乎很差。通常,方法和类的设计最好简单明了,这样每个参数都有意义。换句话说:IMHO,你的第一个设计有两个不同的类,比你的第二个更好,因为它更符合单一责任原则。