【问题标题】:How to extract dependency of concrete type?如何提取具体类型的依赖关系?
【发布时间】:2013-08-22 03:00:11
【问题描述】:

我有一个要求,用户需要配置他们希望在发生错误时收到警报的方式。他们拥有的选项如下:

1.) 在事件日志中创建一个事件

2.) 将电子邮件发送到从用户界面指定的电子邮件

我目前使用的类使用如下接口

interface INotificationSender
{
   virtual void SendMessage(string message);
}

我的接口将由以下 2 个具体类实现

class EmailerNotificationSender: public INotificationSender
{
   string m_EmailAddress;

   public EmailerNotificationSender(string emailAddress)
   {
       m_EmailAddress = emailAddress;
   }
   public virtual void SendMessage(string Message)
   {
      // Send Message to email specified in member variable
   }
} 


class EventLogNotificationSender: public INotificationSender
{
   public virtual void SendMessage(string Message)
   {
      // Log message in event log using
   }

}

我当前的界面代码如下所示

public class MyUserinterfaceWindow
{
    private INotificationSender m_NotificationSender; // concrete type depends on user interface selection!

    public void Button_Click(...)
    {
       if (emailSelected)
       {
           m_NotificationSender = new EmailerNotificationSender(textbox.email)
           return;
       }
        m_NotificationSender = new EventLogNotificationSender();
    }

    public void SendAlert()
    {
        m_NotificationSender.SendMessaged("SOMETHING BAD HAPPENED");
    }
}

总结/问题: 如何删除正在实例化的具体类型的存在(例如 EventLogNotificationSender 和 EmailerNotificationSender )

注意: EventLogNotificationSender 在它的具体构造函数中不需要参数,而 EmailNotificationSender 的具体构造函数需要一个字符串参数!

【问题讨论】:

  • 为什么您的警报配置窗口会有SendAlert() 方法?如果您将用户首选项存储在某处而不是在窗口中硬连线m_NotificationSender,那么您可以根据配置在需要时即时实例化NotificationSenders

标签: design-patterns dependency-injection factory abstract-factory


【解决方案1】:

隐藏composite INotificationSender 实现背后的两个实现。

如果您可以延迟用户选择他想要登录的来源,您可以将该选择逻辑移到复合中,这样可以防止该逻辑混乱MyUserinterfaceWindow。这是一个例子:

public class CompositeNotificationSender : INotificationSender {
    private readonly EmailerNotificationSender mailer;
    private readonly EventLogNotificationSender eventLogger;

    public CompositeNotificationSender(
        EmailerNotificationSender mailer,
        EventLogNotificationSender eventLogger) {
        this.mailer = mailer;
        this.eventLogger = eventLogger;
    }

    public virtual void SendMessage(string Message) {
        bool logToMail = AskUserWhereToLog();
        var logger = logToMail ? this.mailer : this.eventLogger;
        logger.SendMessage(Message);
    }

    private bool AskUserWhereToLog() {
        // TODO:
    }
}

由于此类询问用户,因此与 UI 存在依赖关系,并且此类是特定于 UI 的。如果这不是一个选项,也可以提取它。当然,如果您愿意,您可以通过缓存结果来避免多次询问用户。

如果及时询问用户不起作用,您可以创建一个抽象,CompositeNotificationSenderMyUserinterfaceWindow 都可以依赖它来保存用户的偏好:

public interface IUserLoggingPreference
{
    bool LogToMail { get; set; }
}

MyUserinterfaceWindow(或任何其他地方)现在可以依赖IUserLoggingPreference 并更改LogToMail 值以更改用户的偏好。 CompositeNotificationSender 将如下所示:

public class CompositeNotificationSender : INotificationSender {
    private readonly IUserLoggingPreference preference;
    private readonly EmailerNotificationSender mailer;
    private readonly EventLogNotificationSender eventLogger;

    public CompositeNotificationSender(
        IUserLoggingPreference preference,
        EmailerNotificationSender mailer,
        EventLogNotificationSender eventLogger) {
        this.preference = preference;
        this.mailer = mailer;
        this.eventLogger = eventLogger;
    }

    public virtual void SendMessage(string Message) {
        var logger = this.preference.LogToMail ? this.mailer : this.eventLogger;
        logger.SendMessage(Message);
    }
}

这样用户点击按钮后就不必提示了,CompositeNotificationSender 独立于用户界面,这使得重用这个逻辑更容易(例如在系统的一部分中是没有用户交互的),它使得对这个类进行单元测试更容易。

【讨论】:

    猜你喜欢
    • 2019-05-24
    • 2019-10-13
    • 1970-01-01
    • 2018-11-17
    • 1970-01-01
    • 2014-05-19
    • 2022-08-24
    • 2014-09-06
    • 1970-01-01
    相关资源
    最近更新 更多