如何使我的邮件列表管理器类更加松散耦合?

时间:2011-12-16 09:13:16

标签: php object loose-coupling

我正在开展一个涉及潜在客户优惠的简单项目。该项目将与第三方邮件列表提供商集成,该提供商将使用Prospect对象管理列表中的电子邮件地址,并提供对象来管理广告系列。

我担心的一点是,任何邮件列表提供商(例如MailChimp)可能会决定停止提供服务,或者将来更改条款。我不想让我的软件依赖于提供程序,而是希望它依赖于通用接口,该接口可以使用不同的类重新实现,该类使用不同的邮件列表提供程序。这样,如果确实发生了这样的事情,我只会编写一个新类并实例化代替旧类。

这似乎很容易实现。我的抽象类(包含在下面)定义了抽象方法,这些方法接受Prospect或Offer对象,并为它们执行通用的邮件列表相关函数,在需要时返回true / false或整数值。这个界面应该能够很好地满足我的应用需求。

<?php
/**
 * MailingList file.
 *
 * Contains the class definition for the abstract class Monty_MailingList.
 * @author Lewis Bassett <lewis.bassett@bassettprovidentia.com>
 * @version 0.1
 * @package Monty
 */

/**
 * Represents the interface for all MailingList classes.
 *
 * Adhereing to this interface means that if a MailingList provider
 * (e.g., MailChimp) stops a service, a new class can extend this interface and
 * be replace the obsolete class with no need to modify any of the client code.
 *
 * @author Lewis Bassett <lewis.bassett@bassettprovidentia.com>
 * @version 0.1
 * @package Monty
 * @copyright Copyright (c) 2011, Bassett Providentia
 */
abstract class Monty_MailingList
{
    /**
     * Adds the passed prospect to the mailing list, or returns false if the
     * prospect already exists. Throws an error if the prospect could not be
     * added for any reason (other than it already existing).
     *
     * @param Monty_Prospect $prospect The prospect object to be added to the
     * mailing list.
     * @return bool Whether or not the prospect was added.
     */
    abstract public function addProspect(Monty_Prospect $prospect);

    /**
     * Updates the properties stored on the mailing list of the passed prospect,
     * or returns false if no data was updated. If the prospect is not found, a
     * they are added to the list. Throws an error if the prospect could not be
     * added or updated for any readon.
     *
     * @param Monty_Prospect $prospect The prospect object whose mailing list
     * data is to be updated.
     * @return bool Whether or not the prospect was updated.
     */
    abstract public function updateProspect(Monty_Prospect $prospect);

    /**
     * Returns true if the passed prospect object could be found on the mailing
     * list.
     *
     * @param Monty_Prospect $prospect The prospect object to be searched for.
     * @return bool Whether or not the prospect was found.
     */
    abstract public function findProspect(Monty_Prospect $prospect);

    /**
     * Deletes the passed prospect from the mailing list, or returns false if
     * the passed object is not found on the mailing list.
     *
     * @param Monty_Prospect $prospect The prospect to be deleted.
     * @return bool Whether or not the prospect was deleted.
     */
    abstract public function deleteProspect(Monty_Prospect $prospect);

    /**
     * Creates a campaign for the passed offer object, or returns false if the
     * campaign already exists. Throws an error if the campaign could not be
     * created for any reason (other than it already existing).
     *
     * @param Monty_Offer $offer The offer to be created.
     * @return bool Whether or not the offer was created.
     */
    abstract public function createOffer(Monty_Offer $offer);

    /**
     * Sends the campaign for the passed offer object, or returns false if the
     * campaign could not be sent for a reasonable reason (run out of credit or
     * something). If the campaign does not yet exist, it is created. Throws an
     * error if the campaign could not be created, or an was not sent for an
     * unknown reason.
     *
     * @param Monty_Offer $offer The offer to be sent.
     * @return bool Whether or not the offer was sent.
     */
    abstract public function sendOffer(Monty_Offer $offer);

    /**
     * Returns true if a campaign for the passed offer object could be found on
     * the mailing list.
     *
     * @param Monty_Offer $offer The offer to be searched for,
     * @return bool Whether or not the offer was found.
     */
    abstract public function findOffer(Monty_Offer $offer);

    /**
     * Returns the ammount of opens registered for the passed offer. Throws an
     * error if a campaign is not found for the passed offer.
     *
     * @param Monty_Offer $offer The offer in question.
     * @return int The ammount of registered opens for that offer.
     */
    abstract public function getOfferOpens(Monty_Offer $offer);

    /**
     * Returns the ammount of clicks registered for the passed offer. Throws an
     * error if a campaign is not found for the passed offer.
     *
     * @param Monty_Offer $offer The offer in question.
     * @return int The ammount of registered clicks for that offer.
     */
    abstract public function getOfferClicks(Monty_Offer $offer);

    /**
     * Returns the ammount of bounces registered for the passed offer. Throws an
     * error if a campaign is not found for the passed offer.
     *
     * @param Monty_Offer $offer The offer in question.
     * @return int The ammount of registered bounces for that offer.
     */
    abstract public function getOfferBounces(Monty_Offer $offer);

    /**
     * Returns the ammount of unsubscribes registered for the passed offer.
     * Throws an error if a campaign is not found for the passed offer.
     *
     * @param Monty_Offer $offer The offer in question.
     * @return int The ammount of registered unsubscribes for that offer.
     */
    abstract public function getOfferUnsubscribes(Monty_Offer $offer);
}

这就是困境。

将来,我的应用程序邮件列表提供程序之间传递的数据可能会发生变化,但是,我不希望不得不随时随地更改界面。通过将对象传递给方法,我可以修改方法以在新属性可用时使用它们,而无需在任何地方更改接口。这似乎是一个非常灵活的解决方案。

但是

我想在其他项目中使用此类,可能不一定使用Prospect或Offer类。从类的角度来看,上面的接口似乎过于紧密耦合,因为类依赖于传递给它的对象。

有没有人对如何保持将对象传递给方法的灵活性有任何建议,但有一个我可以轻松地重用于其他项目的类?

非常感谢你们已经阅读了这篇文章!我一直在寻求提高自己的技能,我非常感谢你对我如何做得更好的见解。

3 个答案:

答案 0 :(得分:1)

如果您想要更通用的方法,请创建一个类,您可以在其中添加而非潜在客户,电子邮件而不是商品,即通用界面(任何)邮件列表。然后让你的Monty_MailingList继承通用列表。

答案 1 :(得分:1)

我在某种程度上同意埃米尔的意见。

您正在混淆这些问题。您的课程称为邮件列表,其中不应包含潜在客户和优惠,但包含您要发送的人员和内容。

前景和优惠是业务逻辑模型,可以通过电子邮件将一个表示发送给用户,因为他们可以在呈现到网页时具有其他表示。从邮件中收集统计数据也是分开的。

我不同意的一点是继承点,因为我通常不会这样做。我不会在这里继承任何东西,而是创建单独的类来处理它们的部分并改为使用组合。

答案 2 :(得分:1)

经过一番思考之后,我想出了我认为最好的解决方案,感谢来自设计模式的一些灵感:可重复使用的面向对象软件的元素(Erich Gamma,Richard Helm) ,Ralph Johnson和John Vlissides)。

我现在有两个抽象类:

MailingListRecipient - 定义代表邮件列表收件人的对象的接口。将为此接口编写所有客户端代码,并且不关心此抽象代码的子类实现它。它将具有设置姓名,电子邮件地址以及在邮件列表中添加,更新,删除和查找收件人的方法。

MailingListMessage - 定义将代表邮件列表上的邮件的对象的接口,并将定义一些setter方法和一些操作。同样,将为此接口编写客户端代码,而不关心子类如何实现它。

然后我会有一个抽象的工厂类:

MailingListFactory - 这会在我的客户端代码中创建 MailingListRecipient MailingListMessage 对象。

因此,对于真正的实现,我将创建:

MailChimpRecipient - 代表MailChimp列表中的收件人。此处的代码将遵循 MailingListRecipient 定义的接口,并且该对象在其构造函数中需要API密钥和ListId。

MailChimpMessage - 表示MailChimp列表中的邮件。此处的代码将遵循 MailingListMessage 定义的接口,此对象在其构造函数中也需要API密钥和ListId。

我的客户端代码不会与上述两个对象中的任何一个进行交互。相反,在我的一个设置文件中,我将创建一个对象:

MailChimpFactory - 用于创建MailChimp收件人和邮件。该对象需要API密钥和ListId,然后将这些传递给上述两个类的构造函数,以便创建MailChimp特定对象。

因此,在我的设置代码中,我将创建一个工厂对象:

$mailingListFactory = new MailChimpFactory($apiKey, $listId);

然后,在我的客户端代码中,将创建新的收件人和消息:

$recipient = $mailingListFactory->createMailingListRecipient();

从那时起,它将能够设置并执行操作:

$recipient->setForename('Lewis');
$recipient->setEmailAddress('lewis@example.com');
$recipient->add();

如果MailChimp突然停止了他们的服务,或者我决定使用另一个邮件列表提供者,我将只创建使用新提供者的MailingListRecipient和MailingListMessage的新子类 - 他们的接口将是相同的,并且客户端代码赢了不知道或关心它是不同的。

然后我将创建一个新的MailingListFactory子类,它将创建新类的新Recipient和Message对象。我需要做的就是更改设置文件中的实例化:

$mailingListFactory = new newMailingListProviderFactory($username, $password);

因为我的其余代码是为抽象工厂中定义的接口编写的,所以不需要更改任何其他代码。

使用抽象工厂可确保我不会遇到代码使用mailChimpRecipient对象和newMailingListProviderMessage对象的情况。

这符合我的目标:

Interchangeablity - 我可以交换我的邮件列表类,代码仍然可以像以前一样工作;

可重用性 - 我可以使用这些类并在其他项目中使用它们。

这似乎是最优雅的方式。如果其他人有更好的方式,我很乐意听到它。感谢大家的回复。