1

我正在做一个涉及ProspectsOffers的简单项目。该项目将与第三方邮件列表提供商集成,该提供商将使用 Prospect 对象来管理列表中的电子邮件地址,并使用 Offer 对象来管理活动。

我担心的一个问题是,任何邮件列表提供商(例如 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 类。从类的角度来看,上面的接口似乎耦合得太紧了,因为类依赖于传递给它的对象。

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

非常感谢你读到这里!我一直在寻求提高我的技能,我将非常感谢您对我如何才能做得更好的洞察力。

4

3 回答 3

1

我在某些方面同意 Emil 的观点。

您在这里混合了担忧。您的课程称为邮件列表,其中不应该包含潜在客户和报价,而是要发送给他们的人和内容。

Prospects 和 Offers 是业务逻辑模型,它们可以通过电子邮件将一种表示形式发送给用户,因为它们在呈现到网页时可以具有另一种表示形式。从邮件中收集统计数据也是另一回事。

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

于 2011-12-16T09:38:36.467 回答
1

经过更多思考,我想出了我认为最好的解决方案,这要归功于设计模式的一些灵感:可重用的面向对象软件的元素(Erich Gamma、Richard Helm、Ralph Johnson 和 John Vlissides)。

我现在有两个抽象类:

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

MailingListMessage - 定义将在邮件列表中表示消息的对象的接口,并将定义一些设置方法和一些操作。同样,客户端代码将为此接口编写,并且不会关心子类如何实现它。

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

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

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

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 子类,它将创建新类的新收件人和消息对象。我需要做的就是更改我的设置文件中的实例化:

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

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

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

这符合我的两个目标:

可互换性——我可以交换我的邮件列表类,并且代码仍然可以像以前一样工作;

可重用性——我可以学习这些课程并在其他项目中使用它们。

这似乎是最优雅的方法。如果其他人有更好的方法,我很想听听。谢谢大家的回复。

于 2011-12-19T16:04:12.923 回答
1

如果您想要更通用的方法,请创建一个类,您可以在其中添加人员而不是潜在客户,并添加电子邮件而不是报价,即(任何)邮件列表的通用接口。然后让您的 Monty_MailingList 继承通用列表。

于 2011-12-16T09:17:58.940 回答