Методы / шаблоны для достижения единого принципа ответственности в расширяемой иерархии классов - PullRequest
2 голосов
/ 02 ноября 2010

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

Но предположим, что у вас есть иерархия Invoice классов в разных слоях программного обеспечения:

namespace CoreLayer {
    public class Invoice {
        public virtual void Print() {
            ...
        }
    }
}

namespace CustomizedLayer {
    public class LaborInvoice : Invoice {
        public override void Print() {
            ...
        }
    }

    public class AccountInvoice : Invoice {
        public override void Print() {
            ...
        }
    }
}

Какие методы или шаблоны проектирования могут бытьиспользуется для разделения ответственности за печать?

Идеи:

  • Отдельный класс с очень большим оператором if, который проверяет для каждого подкласса Invoice и запускает соответствующий код печати,Это кажется неправильным.
  • Шаблон посетителя.Проблема состоит в том, что интерфейс посетителя должен существовать на уровне Core со ссылками на классы на уровне Customized.Я хотел бы иметь возможность добавлять новые подклассы в слой Customized с изменением слоя Core.

Ответы [ 3 ]

1 голос
/ 02 ноября 2010

Я думаю, что приведенное ниже решение полезно для C #, оно не имеет расширений if. Как я знаю, использование шаблона visitor не рекомендуется.

public class InvoicePrinterManager
{
     public void Print(AccountInvoice invoice)
     {
         AccountInvoicePrinter p1 = new AccountInvoicePrinter(invoice);
         p1.print();
     }

     public void Print(LaborInvoice invoice)
     {
         LaborInvoicePrinter p1 = new LaborInvoicePrinter(invoice);
         p1.print();
     }
}

public class InvoicePrinter<T> where T : Invoice, new()
{
    T instance;

    public InvoicePrinter(T invoice)
    {
        if (invoice != null)
        {
            this.instance = invoice;
        }
        else
            instance = new T();
    }

    public virtual void Print()
    {
        /// Arrange objects as you want and print them.
    }
}

public class AccountInvoicePrinter : InvoicePrinter<AccountInvoice>
{
    public AccountInvoicePrinter(AccountInvoice invoice)
        : base(invoice)
    { 
    }

    public override void Print()
    {
       /// todo
    }
}

public class LaborInvoicePrinter : InvoicePrinter<LaborInvoice>
{
    public LaborInvoicePrinter(LaborInvoice invoice)
        : base(invoice)
    { 
    }
    public override void Print()
    {
        /// todo: use instance
    }
}

public class Test
{
    public void TestPrint()
    {
        LaborInvoice li = new LaborInvoice();
        InvoicePrintManager printerManager = new InvoicePrintManager();
        printerManager.Print(li);
    }
}
1 голос
/ 02 ноября 2010

Возможно, вы захотите рассмотреть Ациклический Посетитель (PDF) .

1 голос
/ 02 ноября 2010

Вам действительно нужно разделить счета на подклассы?Отличаются ли счета от других вещей, кроме печати?Если нет, то нет необходимости иметь разные типы Invoice, вам просто нужно, чтобы разные экземпляры InvoicePrinter передавались в экземпляр Invoice:

namespace CoreLayer
{
    public class IInvoicePrinter
    {
        void Print(Invoice invoice);
    }

    public class Invoice
    {
    }
}

namespace CustomizedLayer
{
    public class LaborInvoicePrinter : IInvoicePrinter 
    {
        public void Print(Invoice invoice) 
        {
            ...
        }
    }

    public class AccountInvoicePrinter : IInvoicePrinter 
    {
        public void Print(Invoice invoice) 
        {
            ...
        }
    }
}

И у вас должен быть какой-то IoC для предоставленияс правильным InvoicePrinter экземпляром.

...