如何使我的 MailingList 管理器类更加松散耦合?

发布于 2024-12-21 07:37:18 字数 6180 浏览 2 评论 0原文

我正在开发一个涉及前景要约的简单项目。该项目将与第三方邮件列表提供商集成,该提供商将使用 Prospect 对象来管理列表上的电子邮件地址,并使用 Offer 对象来管理营销活动。

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

这似乎很容易实现。我的抽象类(如下所示)定义了抽象方法,这些方法采用 Prospect 或 Offer 对象并对它们执行通用邮件列表相关功能,在需要时返回 true/false 或整数值。这个接口应该可以很好地满足我的应用程序的需求。

<?php
/**
 * MailingList file.
 *
 * Contains the class definition for the abstract class Monty_MailingList.
 * @author Lewis Bassett <[email protected]>
 * @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 <[email protected]>
 * @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 类。从类的角度来看,上面的接口似乎耦合得太紧密,因为类依赖于传递给它的对象。

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

如果您已经读到这里,非常感谢!我一直在寻求提高我的技能,我将非常感谢您对我如何改进这方面的见解。

I am working on a simple project involving Prospects and Offers. This project will integrate with a third-party mailing list provider, which will use Prospect objects to manage email addresses on the list and Offer objects to manage campaigns.

One of my concerns is that any Mailing List provider (e.g., MailChimp) may decide to stop offering their service, or change the terms in the future. Rather than make my software reliant on the provider, I want to make it reliant on a generic interface, which could be reimplemented using a different class which uses a different Mailing List provider. That way, if such a thing did happen, I would simply write a new class and instantiate in place of the old class.

This seems to be easy enough the implement. My abstract class, included below, defines abstract methods which take either Prospect or Offer objects and do generic Mailing List related functions to them, returns true/false or integer values where required. This interface should meet the needs of my application quite well.

<?php
/**
 * MailingList file.
 *
 * Contains the class definition for the abstract class Monty_MailingList.
 * @author Lewis Bassett <[email protected]>
 * @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 <[email protected]>
 * @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);
}

Here comes the dilemma.

In the future, the data that is passed between my application the Mailing List provider may change, however, I don't want to have to keep changing the interface everywhere. By passing objects into the methods, I can modify the methods to use new properties as they become available, without changing the interface anywhere. This seems like a very flexible solution.

But

I would like to use this class in other projects, that might not necessarily use Prospect or Offer classes. The interface above seems too tightly coupled, from the perspective of the class, in that the class is reliant on the objects being passed to it.

Does anyone have any suggestions about how I might keep the flexibilty of passing objects to the methods, but have a class that I can easily reuse for other projects?

Many thanks if you have read this far! I am always looking to improve my skills and I'll be very grateful for your insight into how I might make this better.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

你的背包 2024-12-28 07:37:18

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

If you want a more generic approach, create a class where you add people instead of prospects, and email messages instead of offers, i.e., a generic interface to (any) mailing list. Then make your Monty_MailingList inherit the generic list.

不弃不离 2024-12-28 07:37:18

我在某些方面同意埃米尔的观点。

你在这里混合了担忧。您的类称为邮件列表,其中不应包含潜在客户和优惠,但包含您想要发送给他们的人员和内容。

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

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

I agree with Emil in some part.

You are mixing the concerns here. Your class is called mailing list which should have nothing with the prospects and offers, but with people and content you want to send them.

Prospects and Offers are business logic models which can have one representation sent via email to users, as they can have the other representation when rendered to the web page. Collecting stats from mails is separate thing as well.

One thing I don't agree is on the inheritance point, as I never do usually. I would not inherit anything here but create separate classes that handle their part and use composition instead.

吃不饱 2024-12-28 07:37:18

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

我现在有两个抽象类:

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

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

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

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('[email protected]');
$recipient->add();

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

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

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

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

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

这满足了我的两个目标:

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

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

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

After some more thought, I've come up with what I think is the best solution, thanks to some inspiration from Design patterns : elements of reusable object-oriented software (Erich Gamma, Richard Helm, Ralph Johnson and John Vlissides).

I now have two abstract classes:

MailingListRecipient - defines the interface for objects that will represent a recipient of the mailing list. All client code will be written for this interface, and won't care what child class of this abstract code implements it. It will have methods to set a forename, surname and email address and to add, update, remove and find the recipient on the mailing list.

MailingListMessage - defines the interface for objects that will represent a message on the mailing list, and will have some setter methods and some actions defined. Again, client code will be written for this interface, and won't care how a subclass implements it.

I will then have an abstract factory class:

MailingListFactory - this creates MailingListRecipient and MailingListMessage objects throughout my client code.

So, for a real implementation, I will create:

MailChimpRecipient - to represent a recipient on a MailChimp list. Code in here will adhere to the interface defined by MailingListRecipient, and the object will need an API key and ListId in its constructor.

MailChimpMessage - to represent a message on a MailChimp list. Code here will adhere to the interface defined by MailingListMessage, and this object too will need an API key and ListId in its constructor.

My client code won't interact with either of the two objects above. Instead, within one of my settings files, I'll create an object of:

MailChimpFactory - used to create MailChimp recipients and messages. The object will need the API key and ListId, and will in turn pass these to the constructors of the above two classes in order to create MailChimp specific objects.

So, in my settings code I'll create a factory object:

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

Then, throughout my client code, new Recipients and Messages will be created thus:

$recipient = $mailingListFactory->createMailingListRecipient();

From then on, it will be able to set things and do actions:

$recipient->setForename('Lewis');
$recipient->setEmailAddress('[email protected]');
$recipient->add();

If MailChimp suddenly stop their service, or I decide to use another mailing list provider, I'll just create new child classes of MailingListRecipient and MailingListMessage that use a new provider - their interfaces will be the same, and the client code won't know or care that it's different.

I'll then create a new child class of MailingListFactory that will create new Recipient and Message objects of the new classes. All I'll need to do is change the instantiation in my settings file:

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

Because the rest of my code is written for the interfaces defined in my abstract factories, nothing else will need to be changed.

Using an abstract factory ensures that I won't ever get into a situation where code is using mailChimpRecipient objects and newMailingListProviderMessage objects.

This meets both my objectives:

Interchangeablity - I can swap my mailing list classes and the code will still work as before;

Reusability - I can take those classes and use them in other projects.

This seems the most elegant way to do this. If anyone else has a better way, I'd love to hear about it. Thanks for your replies everyone.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文