0

I need to design a receipt object that will take line items in a transaction and format them for a 40 column display. In the future I will also need another format for regular sized printer paper.

The transaction has different types of line items (items, comments, discounts, tenders, etc.).

My first thought was to create an interface for each of these so that they would be responsible for formatting themselves, and I could add a new method to the interface for each type of receipt format I need. My next thought was to create one class for each type of receipt format I need and let it be responsible for looking at what type each line is and formatting it appropriately.

So my question is whether there is a better design pattern that I may be overlooking, and if not then is there significant reason to favor one of the above designs over the other?

So I could add something like this:

public interface IReceiptFormat
{
    string FormatFor40Column();
    string FormatForRegularPaper();
}

to my Item, Comment, etc. classes. Or I could create something like this:

public ReceiptFormatterFor40Column
{
    public Ticket Ticket {get; private set;}

    public ReceiptFormatterFor40Column(Ticket ticket)
    {
        Ticket = ticket;
    }

    public List<string> GenerateReceipt()
    {
        var lines = new List<string>();

        foreach(var line in Ticket.Lines)
        {
            // check what type of object line is and add
            // add an appropriately formatted string to lines
        }
    }
}
Charles
  • 50,943
  • 13
  • 104
  • 142
Brandon Moore
  • 8,590
  • 15
  • 65
  • 120

3 Answers3

1

My initial thoughts are that a Strategy pattern is what you're looking for. This would be useful if you wanted to separate the formatting from the content. The line could be injected with its appropriate formatting strategy on creation, and maybe have a default for convenience. Then the lines could all implement a single IFormattable interface that delegated to the injected strategy.

David Osborne
  • 6,436
  • 1
  • 21
  • 35
  • Well, I may be getting a bit ahead of myself here but theoretically if I were to add food industry type features then I might be running multiple strategies at the same time... a receipt for the customer and one for the kitchen, and maybe even another for a delivery guy. That would be pretty far down the road, but I did work for a restaurant POS company that did that in the past. That said, would you try to adapt the Strategy pattern to handle that or would you go another direction? – Brandon Moore Jan 25 '13 at 09:56
  • After thinking about this some more, it 'seems' to me that the strategy pattern is more useful when there is one specific strategy a particular object should use rather than a range of strategies that it could use as in the above comment, or more practically for me: I may create an invoice using 8.5x11 paper and then later give the customer a 40 col receipt when they pay. – Brandon Moore Jan 25 '13 at 11:24
  • "...the strategy pattern is more useful when there is one specific strategy a particular object..." This is why I suggested injecting the printing strategy into the line. Then your foreach doesn't need to test what type of line you have, you just invoke the Print/Format/Whatever method on the common interface and the line handles the rest. – David Osborne Jan 25 '13 at 13:31
  • Okay, so you're talking about the strategy for the particular line type whereas I was thinking about the strategy for which kind of printer it's going to. – Brandon Moore Jan 26 '13 at 09:00
  • Even though I presented the different line types as their own classes in my OP, I'm actually leaning now towards not doing that. As I started to implement that approach I basically realized it wasn't really simplifying anything but it was actually adding complexity. Having said that, this wouldn't prevent me from applying the same principles but I think the strategy of the different line types is tied to the strategy of the printer so it doesn't make sense to inject the strategy when I don't yet know which one will be chosen. – Brandon Moore Jan 26 '13 at 10:47
  • That is to say, I don't see how I could separate the strategy of the different line types from which printer it's for. I could make the line type strategy interface include each printer strategy, but I think that starts to get more complicated than it needs to be. – Brandon Moore Jan 26 '13 at 10:50
  • Even though I didn't go with your answer, you definitely helped me think it through more thoroughly and this could easily be the right answer for the next guy who has slightly different requirements than me, so +1 for the thoughts. – Brandon Moore Jan 26 '13 at 10:52
1

Why don't you just create a IRecieptPrinter interface and then a RegularPaperPrinter and ColumnPrinter?

then you could do:

var printer = new RegularPaperPrinter();
printer.Print(receipt);

If you don't want to create the printers yourself you could use the factory pattern:

var printer = printerFactory.Create("regularpaper");
printer.Print(receipt);
jgauffin
  • 99,844
  • 45
  • 235
  • 372
  • Yeah, that's where I would have ended up with my second idea. I know I didn't put it in my post, but after having written one implementation there's no doubt in my mind I would refactor my code into exactly this once I got ready to start on the next implementation. – Brandon Moore Jan 25 '13 at 11:10
  • You know I never was good at the brainstorming stage in writing English papers, but I did always 'refactor' as I went and I've never written more than one draft of a major paper (on which I always got A's). Of course, then I had to go back and write fake rough drafts to turn in with it :) – Brandon Moore Jan 25 '13 at 11:13
  • I recently listened to Tommy Emmanuel talk about having a dream of Chet Atkins appearing on stage and playing something he'd been trying hard to figure out. So I figure it's only right I should at least give Jon Skeet an opportunity to give me the answer in the next few hours as I sleep. If not, then I'll mark it (: – Brandon Moore Jan 25 '13 at 12:01
0

I had the same case to solve and I finaly used a bridge design pattern separating the receipt and his content from the printing.

abstract class Receipt{
private Printer printer;

public Receipt (Printer printer){
if (printer==null){
throw new NullArgumentException ("Printer can't be null.");
}
this.printer=printer;
}

public abstract PrintJobStatus printReceipt ();
}

abstract class Printer {

public abstract void printLine (String txt, Alignement al, Font fo, Size si);
public abstract void printQrCode(String qrCodeStr, QrType qrType, Size si);
}

public class VatReceipt extend Receipt{

public VatReceipt (Printer printer){
super (printer);
}

@Overide
public PrintJobStatus printReceipt ()
PrintJobStatus result=PrintJobStatus.UNDEFINE;

printer.printLine ("Hello i'm the receipt.header",
Alignement.CENTER,  Font.A, Size.SMALL);
...
...
return result;
}

public class EpsonT88Printer extend Printer{
private EpsonPrinterSdk epsonSdkPrinter;

@Overide
public void printLine(String txt, Alignement al, Font fo, Size si){
int epsonAlignement=convertGenericAligToEpsonAlig(al);
int epsonFont=convertGenericFontToEpsonFont(fo);
...
epsonSdkPrinter.printTextLine (txt, epsonAlignement,...);
}
}

I'm answering from my phone...

mofobo
  • 7
  • 3