【问题标题】:Looking for direction on a design pattern for a receipt object寻找收据对象的设计模式方向
【发布时间】:2013-01-25 08:43:40
【问题描述】:

我需要设计一个收据对象,该对象将在交易中获取行项目并将它们格式化为 40 列显示。将来我还需要另一种格式的常规尺寸打印纸。

交易有不同类型的行项目(项目、cmets、折扣、投标等)。

我的第一个想法是为其中的每一个创建一个接口,以便他们负责格式化自己,并且我可以为我需要的每种收据格式的接口添加一个新方法。我的下一个想法是为我需要的每种收据格式创建一个类,并让它负责查看每行的类型并进行适当的格式化。

所以我的问题是是否存在我可能忽略的更好的设计模式,如果没有,那么是否有充分的理由支持上述设计中的一种而不是另一种?

所以我可以添加如下内容:

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

到我的项目、评论等类。或者我可以创建这样的东西:

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
        }
    }
}

【问题讨论】:

    标签: design-patterns point-of-sale


    【解决方案1】:

    为什么不直接创建一个IRecieptPrinter 接口,然后创建一个RegularPaperPrinterColumnPrinter

    那么你可以这样做:

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

    如果您不想自己创建打印机,可以使用工厂模式:

    var printer = printerFactory.Create("regularpaper");
    printer.Print(receipt);
    

    【讨论】:

    • 是的,这就是我的第二个想法。我知道我没有把它放在我的帖子中,但是在编写了一个实现之后,毫无疑问,一旦我准备好开始下一个实现,我就会将我的代码重构为这个。
    • 你知道我在写英文论文的头脑风暴阶段从来都不擅长,但我总是在写英文论文时“重构”,而且我从来没有写过一篇主要论文的草稿(关于我总是得A)。当然,然后我不得不回去写假草稿才能上交:)
    • 我最近听了 Tommy Emmanuel 谈到梦想 Chet Atkins 出现在舞台上并演奏他一直在努力弄清楚的东西。所以我认为我应该至少给 Jon Skeet 一个机会,让我在接下来的几个小时内在我睡觉的时候给我答案。如果没有,那么我会标记它(:
    【解决方案2】:

    我最初的想法是您正在寻找一种策略模式。如果您想将格式与内容分开,这将很有用。该行可以在创建时注入其适当的格式化策略,并且为方便起见可能具有默认值。然后这些行都可以实现一个委托给注入策略的单个 IFormattable 接口。

    【讨论】:

    • 好吧,我可能有点超前了,但理论上如果我要添加食品行业类型的功能,那么我可能会同时运行多种策略......客户收据一个用于厨房,甚至可能另一个用于送货员。那将是相当遥远的道路,但我确实为过去这样做的一家餐厅 POS 公司工作。也就是说,你会尝试调整策略模式来处理这个问题,还是会转向另一个方向?
    • 在考虑了更多之后,在我看来,策略模式在特定对象应该使用一个特定策略而不是它可以用作的一系列策略时更有用在上面的评论中,或者对我来说更实际的是:我可以使用 8.5x11 纸张创建发票,然后在客户付款时给他们一张 40 col 的收据。
    • "...当特定对象有一个特定策略时,策略模式更有用..." 这就是为什么我建议将打印策略注入该行的原因。然后你的 foreach 不需要测试你有什么类型的线,你只需在公共接口上调用 Print/Format/Whatever 方法,剩下的就由该线处理。
    • 好的,你说的是针对特定线型的策略,而我在考虑针对哪种打印机的策略。
    • 尽管我在我的 OP 中将不同的线型作为它们自己的类来表示,但我现在实际上倾向于不这样做。当我开始实施这种方法时,我基本上意识到它并没有真正简化任何事情,而是实际上增加了复杂性。话虽如此,这不会阻止我应用相同的原则,但我认为不同线型的策略与打印机的策略相关,所以当我还没有注入策略时,注入策略是没有意义的知道会选择哪一个。
    【解决方案3】:

    我有同样的情况要解决,最后我使用了一个桥接设计模式,将收据和他的内容与打印分开。

    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,...);
    }
    }
    

    我正在用手机接听...

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-01-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-06-28
      相关资源
      最近更新 更多