在代码中切换电子邮件提供商的设计模式
design pattern for switching email providers in the code
我们需要在 php 应用程序中发送电子邮件(谁不需要)。最初当我们的应用程序处于婴儿期时,我们只使用 linux sendmail。
向前一点,我们切换到我们自己的 SMTP 服务器。这意味着每个具有电子邮件相关功能的文件中的代码更改。
一年后,我们转向 AWS,不得不再次更改代码以开始使用 AWS 电子邮件服务。
现在我们转移到 Google 云,再次更改电子邮件代码以使用某些第三方提供商。
电子邮件配置遍布各处,更改一个提供商意味着需要更新数百个文件,如果您错过一个,客户可能无法收到一部分的电子邮件,但可以收到另一部分的电子邮件.
我只是退后一步,意识到我们正在更改的代码没有任何意义,只是因为我们的电子邮件提供商已更改。
但我这辈子都想不出解决这个问题的方法。
我需要做的就是从我的代码中删除电子邮件并重构它,使我的应用程序代码可以独立于邮件服务提供商运行。
她是我对此的看法
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() 的内容,我很高兴。
我做的对吗?
这是一个合适的策略模式吗?
只要能解决我的问题,我还需要关心它是什么模式吗?
有没有更好的方法让自己摆脱这个日益严重的混乱局面?
是的,基本上您做对了 -- 鉴于您 objective 在需要更改邮件提供商时可以轻松修改代码。
但是,您可以使您的设计更简单更好。
查看EmailGateway
class:它没有做任何重要的事情。它与 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
方法),您只需创建 one 实例 EmailProvider
需要:
EmailProvider emailProvider = new GoogleEmailProvider("username", "password");
然后您可以将该实例传递到任何需要发送电子邮件的地方:
Foo foo = new Foo(emailProvider);
这种设计有一些好处。首先,像 Foo
这样的 class 单元测试更容易。你总是可以写一个 MockEmailProvider
并将它传递给 Foo
。其次,像 Foo
这样的 classes 的用户应该很容易意识到 Foo
可以通过查看其签名来发送电子邮件。发邮件,做IO/Database/Network……都是很重要的事情,应该时刻注意。
希望对您有所帮助。
我们需要在 php 应用程序中发送电子邮件(谁不需要)。最初当我们的应用程序处于婴儿期时,我们只使用 linux sendmail。 向前一点,我们切换到我们自己的 SMTP 服务器。这意味着每个具有电子邮件相关功能的文件中的代码更改。 一年后,我们转向 AWS,不得不再次更改代码以开始使用 AWS 电子邮件服务。 现在我们转移到 Google 云,再次更改电子邮件代码以使用某些第三方提供商。
电子邮件配置遍布各处,更改一个提供商意味着需要更新数百个文件,如果您错过一个,客户可能无法收到一部分的电子邮件,但可以收到另一部分的电子邮件.
我只是退后一步,意识到我们正在更改的代码没有任何意义,只是因为我们的电子邮件提供商已更改。
但我这辈子都想不出解决这个问题的方法。
我需要做的就是从我的代码中删除电子邮件并重构它,使我的应用程序代码可以独立于邮件服务提供商运行。
她是我对此的看法
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() 的内容,我很高兴。
我做的对吗? 这是一个合适的策略模式吗? 只要能解决我的问题,我还需要关心它是什么模式吗?
有没有更好的方法让自己摆脱这个日益严重的混乱局面?
是的,基本上您做对了 -- 鉴于您 objective 在需要更改邮件提供商时可以轻松修改代码。
但是,您可以使您的设计更简单更好。
查看EmailGateway
class:它没有做任何重要的事情。它与 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
方法),您只需创建 one 实例 EmailProvider
需要:
EmailProvider emailProvider = new GoogleEmailProvider("username", "password");
然后您可以将该实例传递到任何需要发送电子邮件的地方:
Foo foo = new Foo(emailProvider);
这种设计有一些好处。首先,像 Foo
这样的 class 单元测试更容易。你总是可以写一个 MockEmailProvider
并将它传递给 Foo
。其次,像 Foo
这样的 classes 的用户应该很容易意识到 Foo
可以通过查看其签名来发送电子邮件。发邮件,做IO/Database/Network……都是很重要的事情,应该时刻注意。
希望对您有所帮助。