在可扩展的类层次结构中实现单一职责原则的技术/模式

发布于 2024-09-29 21:03:28 字数 739 浏览 10 评论 0原文

例如,单一职责原则规定,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 的每个子类并运行适当的打印代码。这似乎是错误的。
  • 访客模式。问题是核心层中需要存在访问者接口,并引用定制层中的类。我希望能够通过修改核心层在定制层中添加新的子类。

The Single Responsibility Principle says, for example, that an Invoice class should not contain the code to print itself. Printing should be separated out into a different class.

But suppose you have a hierarchy of Invoice classes in different layers of the software:

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() {
            ...
        }
    }
}

What techniques or design patterns can be used to separate out the printing reponsibility?

Ideas:

  • A separate class with a great big if statement that tests for each subclass of Invoice and runs appropriate printing code. This seems wrong.
  • Visitor pattern. The problem is that a visitor interface would need to exist in the Core layer with references to the classes in the Customized layer. I would like to be able to add new subclasses in the Customized layer with modifying the Core layer.

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

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

发布评论

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

评论(3

高跟鞋的旋律 2024-10-06 21:03:33

我认为下面的解决方案对 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);
    }
}

I think the bellow solution is useful for C#, it has no extera if, As I know, use of visitor pattern is not recommanded.

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);
    }
}
无人接听 2024-10-06 21:03:32

您可能需要考虑非循环访问者 (PDF)

You might want to consider the Acyclic Visitor (PDF).

執念 2024-10-06 21:03:32

您真的需要对发票进行子类化吗?除了打印之外,发票还有其他区别吗?如果不是,则无需具有不同的 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 实例。

Do you really need to subclass invoices? Do invoices differ in other things than printing? If no, there's no need to have different Invoice types, you just need different InvoicePrinter types passed to the Invoice instance:

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) 
        {
            ...
        }
    }
}

And you should have some kind of IoC to provide you with proper InvoicePrinter instance.

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