Techniques/patterns to achive Single Responsibility Principle in an extendable class hierarchy
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
st开发者_如何学运维atement that tests for each subclass ofInvoice
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.
You might want to consider the Acyclic Visitor (PDF).
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.
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);
}
}
精彩评论