在.Net中使用接口进行标准化

时间:2014-09-29 13:54:13

标签: c# oop interface

我有一个关于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. 编写条件以检查从方法返回哪个管理器,并使用适当的值发送该管理器的必需参数,并将其他人发送为空。
  2. 使用适当的值发送所有参数,无论管理员如何,因此它们都可以正常工作。但是将来,如果添加了另一个经理,那么我每次都需要为该经理发送额外的参数。
  3. 还有另一种我无法思考的方式吗?
  4. 除SendMessage之外的其他方法根据管理器可能需要不同的参数,这些参数在运行时是未知的。例如:

    MessageSenderViaMailManager AddContact 方法可能需要以下参数:

    • 联系人姓名
    • 联系邮件
    • 联系电话号码
    • 联系MailType(丰富,简单)

    MessageSenderViaUserNameManager AddContact 方法可能需要以下参数:

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

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

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

    GitHub编辑:

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

4 个答案:

答案 0 :(得分:1)

为什么不按如下方式界面:

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

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

您可以随时在工厂中实施检查,以便知道要创建哪个类。

答案 1 :(得分:1)

我会创建一个包含收件人属性的class MailMessage。然后在MailMessage类上有2个AddRecipient方法的重载。每种方式都有一个用于添加收件人。

答案 2 :(得分:1)

更新: 为了清晰起见,以前的答案已经过修改。

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

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例子中的情况):

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

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

然后发送消息:

manager.SendMessage(message);

免责声明: 代码未经过测试。

答案 3 :(得分:1)

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

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

尽管如此,我同意你所选择的抽象(一种具有多个参数的方法,根据客户端具有哪些信息而可选择或相互排斥),似乎很差。通常,方法和类的设计最简单直接,因此每个参数都有意义。换句话说:你的第一个设计有两个不同的类,恕我直言,比你的第二个更好,因为它更好地与Single Responsibility Principle对齐。