Как сделать класс менеджера MailingList более свободным? - PullRequest
1 голос
/ 16 декабря 2011

Я работаю над простым проектом, включающим Перспективы и Предложения . Этот проект будет интегрирован со сторонним поставщиком почтовой рассылки, который будет использовать объекты 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. Представленный выше интерфейс кажется слишком тесно связанным с точки зрения класса в том смысле, что класс зависит от передаваемых ему объектов.

Есть ли у кого-нибудь предложения о том, как я могу сохранить гибкость передачи объектов в методы, но есть класс, который я могу легко использовать для других проектов?

Большое спасибо, если вы прочитали это далеко! Я всегда стараюсь улучшить свои навыки и буду очень благодарен за ваше понимание того, как я могу сделать это лучше.

Ответы [ 3 ]

1 голос
/ 16 декабря 2011

Я согласен с Эмилем в какой-то части.

Вы смешиваете проблемы здесь.Ваш класс называется списком рассылки, который не должен иметь ничего общего с потенциальными клиентами и предложениями, но с людьми и контентом, которые вы хотите отправить.

Перспективы и предложения - это модели бизнес-логики, которые могут иметь одно представление, отправленное пользователям по электронной почте, поскольку они могут иметь другое представление при отображении на веб-странице.Сбор статистики из писем - это тоже отдельная вещь.

Одна вещь, с которой я не согласен, это вопрос наследования, как я обычно не делаю.Я бы не стал наследовать здесь ничего, кроме создания отдельных классов, которые обрабатывают свою часть и используют вместо этого композицию.

1 голос
/ 19 декабря 2011

После некоторых размышлений я пришел к тому, что я считаю лучшим решением, благодаря некоторому вдохновению от Шаблоны проектирования: элементы многоразового объектно-ориентированного программного обеспечения (Эрих Гамма, Ричард Хелм,Ральф Джонсон и Джон Влиссидес).

Теперь у меня есть два абстрактных класса:

MailingListRecipient - определяет интерфейс для объектов, которые будут представлять получателя списка рассылки.Весь клиентский код будет написан для этого интерфейса, и ему все равно, какой дочерний класс этого абстрактного кода его реализует.У него будут методы для установки имени, фамилии и адреса электронной почты, а также для добавления, обновления, удаления и поиска получателя в списке рассылки.

MailingListMessage - определяет интерфейс для объектов, которые будутпредставляет сообщение в списке рассылки, и в нем будут определены некоторые методы установки и определены некоторые действия.Опять же, клиентский код будет написан для этого интерфейса, и ему будет все равно, как его реализует подкласс.

Тогда у меня будет абстрактный класс фабрики:

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- Я могу поменять местами свои списки рассылки, и код все равно будет работать, как и раньше;

Повторное использование - я могу взять эти классы и использовать их в других проектах.

Это кажется наиболее элегантным способомсделай это.Если у кого-то есть способ получше, я бы хотел услышать об этом.Спасибо всем за ваши ответы.

1 голос
/ 16 декабря 2011

Если вам нужен более общий подход, создайте класс, в который вы добавляете людей вместо потенциальных клиентов и сообщения электронной почты вместо предложений, т. Е. Универсальный интерфейс для (любого) список рассылки. Затем заставьте ваш Monty_MailingList наследовать общий список.

...