Эффективное использование шаблона Decorator - PullRequest
0 голосов
/ 11 декабря 2011

Я разрабатываю библиотеку, которая обеспечивает доступ к приложению Bug Tracker, используемому нашей компанией.

В настоящее время нам просто необходим доступ к простым функциям:

  1. Open Defect
  2. Дефект запроса (поиск по заданным критериям)
  3. Подключение / отключение от Bug Tracker

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

Для поддержки большего количества операций в будущем мы могли бы:

  1. Расширить интерфейс;Заставьте все реализующие классы реализовать добавленные члены.
  2. Используйте шаблон Decorator для добавления операций / функциональности во время выполнения.

Проблема в том, что Decorator, похоже, слишком привязан к базовомуБазовый класс / интерфейс.Я имею в виду, что он опирается на тот факт, что декорируемый объект обеспечивает достаточный доступ, чтобы позволить ему легко добавлять операции.

В этом примере, если я НЕ выставляю базовый сторонний объект, предоставляющий API длятрекер ошибок в моем интерфейсе, декоратор не сможет добавить больше операций.

Как мне решить эту проблему, разработав ее лучше?

1 Ответ

3 голосов
/ 11 декабря 2011

Вам не нужен шаблон Decorator

Шаблон декоратора не очень полезен для этого сценария. Его цель - позволить вам добавлять поведения во время выполнения . Вы пытаетесь добавить поведение (более или менее) во время компиляции .

См .: http://en.wikipedia.org/wiki/Decorator_pattern

Этот шаблон позволяет пользователю создавать следующие типы экземпляров вашего кода:

  • Преднамеренно неокрашенные типы, имеющие только базовое поведение
  • Произвольно оформленные типы, позволяющие обеспечить переопределенное поведение

Чтобы сделать это, нужно много времени на разработку. Только для одного украшения вам нужно реализовать четыре класса.

Это много накладных расходов, если вы не заботитесь о создании намеренно недекорированных типов.

Возможно, вам вообще не нужно об этом беспокоиться

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

Несколько интерфейсов лучше подходят для вашего сценария

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

При выпуске новой версии, которая изменяет интерфейс, добавьте новый интерфейс и реализуйте оба. Заставьте ваши объекты реализовывать оба интерфейса.

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

Вот пример. Обратите внимание, что я вообще не продумывал дизайн услуг, за исключением решения вашей конкретной проблемы:

namespace Version1
{
    public interface IOpenDefectService
    {
        void Submit(string title, string description, int severity);
        void Bump(int issueId);
    }
}

namespace Version2
{
    public interface IOpenDefectService
    {
        void Submit(string title, string description, int severity);

        // Removed Bump method - it was a bad idea

        // Added an optional priority field
        void Submit(string title, string description, int severity,
            int priority);

        // Added support for deleting 
        void Delete(int id);
    }
}

public class OpenDefectService : Version1.IOpenDefectService,
    Version2.IOpenDefectService
{
    // Still must support it until you no longer have any clients using it.
    // Here to support staggered rollouts
    [Obsolete("This method is deprecated.  Don't use it")]
    public void Bump(int issueId) { /* Still has implementation... */ }

    public void Submit(string title, string description,
        int severity) { /* ... */ }
    public void Submit(string title, string description,
        int severity, int priority) { /* ... */ }
    public void Delete(int id) { /* ... */ }
}
...