代码中切换电子邮件提供者的设计模式

时间:2018-11-20 02:12:11

标签: php design-patterns refactoring strategy-pattern

我们需要在我们的php应用程序中发送电子邮件(不发送)。最初,当我们的应用处于起步阶段时,我们仅使用linux sendmail。 往前走了一点,我们切换到了自己的SMTP服务器。这意味着每个具有电子邮件相关功能的文件中的代码都会更改。 一年后,我们转到了AWS,不得不再次更改代码以开始使用AWS电子邮件服务。 现在,我们移至Google云,再次将电子邮件代码更改为使用某些第三方提供商。

电子邮件的配置遍布各地,更改一个提供程序意味着需要更新数百个文件,如果您错过其中一个,则客户端可能无法一方面接收电子邮件,但可以接收另一部分的电子邮件。 / p>

我只是退后一步,意识到我们正在更改仅由于我们的电子邮件提供商已更改而没有意义的代码。

但是对于我的一生,我无法弄清楚如何摆脱这种混乱局面。

我所需要做的就是从代码中删除电子邮件,并对其进行重构,以使我的应用程序代码可以独立于邮件服务提供商运行。

这是我的看法

class EmailGateway()
{
  private $emailer;
  public function __construct($someEmailProvider)
  {
     $this->emailer = $someEmailProvider;
  }

  public function send($from, $to, $subject, $bodyText, $bodyHtml="")
  {

    this->emailer->send($from, $to, $subject, $bodyText, $bodyHtml="");
  }
}

然后在我的代码中,我只需要像

那样调用它即可
# Gmail?
$mailGateway = new EmailGateway(new GmailEmailer("username", "password"));

# local?
$mailGateway = new EmailGateway(new SendMailer());

# SMTP?
$mailGateway = new EmailGateway(new MyMailServerMailer("192.168.0.3", "no_user", "secret_password"));

在我的应用程序代码中,我什至可以更进一步,并致电工厂以获取当前的默认电子邮件提供商

class EmailService()
{
   public static function currentProvider(): EmailProviderInterface
   {

      return new MyMailServerMailer("192.168.0.3", "no_user", "secret_password");

   }
}

这将使我上面的呼叫者代码变得更加简单

# SMTP?/GMAIL?/Local?/Whatever
$mailGateway = new EmailGateway(EmailService::currentProvider());

因此,每当需要更改提供程序时,我要做的就是更改currentProvider()的勇气,我很高兴。

我做对了吗? 这是一个合适的策略模式吗? 只要能解决我的问题,我是否应该关心它是什么模式?

是否有更好的方法使自己摆脱这种日益增加的混乱局面?

1 个答案:

答案 0 :(得分:1)

是的,基本上您做对了-鉴于您的目标是在需要更改邮件提供者时轻松修改代码。

但是,您可以使设计更简单,更好。

查看EmailGateway类:它没有做任何重要的事情。它与EmailProvider具有相同的接口,仅将send任务委托给EmailProvider

所以您可能只有一个名为EmailProvider的接口-我使用的是Java,但是应该很容易转换为PHP:

interface EmailProvider {
    void send(String from, String to, String subject, String bodyText, String bodyHtml);
}

然后是几个实现:

class GoogleEmailProvider implements EmailProvider {
    public GoogleEmailProvider (String username, String password) {
        ...
    }
    public void send(String from, String to, String subject, String bodyText, String bodyHtml) {
        ...
    }
}
// and so on ...

在应用程序的CompositionRoot(如main方法),您只需创建所需的EmailProvider一个实例:

EmailProvider emailProvider = new GoogleEmailProvider("username", "password");

然后,您可以将该实例传递到需要发送电子邮件的任何地方:

Foo foo = new Foo(emailProvider);

此设计提供了一些好处。首先,更容易对Foo之类的类进行单元测试。您始终可以编写一个MockEmailProvider并将其传递给Foo。其次,诸如Foo之类的用户应该容易意识到,Foo仅通过查看其签名即可发送电子邮件。发送电子邮件,进行IO /数据库/网络...是很重要的事情,应该始终保持警惕。

希望这会有所帮助。