1

I've been working on learning how to use interfaces correctly in c# and I think I mostly understand how they should be used but still feel confused about certain things.

I want to create a program that will create a CSV from Sales Orders or Invoices. Since they are both very similar I figured I could create an IDocument interface that could be used to make a CSV document.

class Invoice : IDocument
{
    public Address billingAddress { get; set; }
    public Address shippingAddress { get; set; }
    public Customer customer { get; set; }
    public List<DocumentLine> lines { get; set; } 
    // other class specific info for invoice goes here       
}

I can create a method CreateCSV(IDocument) but how would I deal with the few fields that differ from Sales Orders and Invoices? Is this a bad use of interfaces?

Sam Marion
  • 690
  • 1
  • 4
  • 17
  • 1
    An interface is just a contract to implement specific methods and properties. Each individual class is responsible for deciding how to implement `CreateCSV()`. See also: [base class vs interface](http://stackoverflow.com/questions/56867/interface-vs-base-class) – stephen.vakil Oct 04 '16 at 17:16
  • Ok thanks that link was very helpful. I feel like using an Interface here would not be the correct way to go about this problem. Instead I'll use abstract class. Thanks. – Sam Marion Oct 04 '16 at 17:24

1 Answers1

3

You don't inherit interfaces, you implement them; and in this case the interface is an abstraction; it says "all things that implement this interface have the following common characteristics (properties, methods, etc)"

In your case, you have found that in fact Invoices and Sales Orders don't quite share the exact same characteristics. Therefore from the point of view of representing them in CSV format, it's not a great abstraction (although for other things, like calculating the value of the document, it's an excellent one)

There are a number of ways you can work around this though, here are two (of many)

Delegate the work to the classes

You can declare an ICanDoCSVToo interface that returns the document in some kind of structure that represents CSV (let's say a CSVFormat class that wraps a collection of Fields and Values). Then you can implement this on both Invoices and Sales Orders, specifically for those use cases, and when you want to turn either of them into CSV format, you pass them by the ICanDoCSVToo interface.

However I personally don't like that as you don't really want your Business Logic mixed up with your export/formatting logic - that's a violation of the SRP. Note you can achieve the same effect with abstract classes but ultimately it's the same concept - you allow someone to tell the class that knows about itself, to do the work.

Delegate the work to specialised objects via a factory

You can also create a Factory class - let's say a CSVFormatterFactory, which given an IDocument object figures out which formatter to return - here is a simple example

public class CSVFormatterLibrary
{
     public ICSVFormatter GetFormatter(IDocument document)
     {
       //we've added DocType to IDocument to identify the document type.
        if(document.DocType==DocumentTypes.Invoice)
        {
             return new InvoiceCSVFormatter(document);
        }
        if (document.DocType==DocumentTypes.SalesOrders)
        {
            return new SalesOrderCSVFormatter(document);
        }
        //And so on
     }
}

In reality, you'd might make this generic and use an IOC library to worry about which concrete implementation you would return, but it's the same concept.

The individual formatters themselves can then cast the IDocument to the correct concrete type, and then do whatever is specifically required to produce a CSV representation of that specialised type.

There are other ways to handle this as well, but the factory option is reasonably simple and should get you up and running whilst you consider the other options.

Community
  • 1
  • 1
Stephen Byrne
  • 7,400
  • 1
  • 31
  • 51
  • Thanks so much for the response. I'm starting to think using an interface for this problem will needlessly complicate it? If I will have to create new classes for each document type I don't see the advantage of using the interface. I guess from here each CSV formatter could be an abstract class since i know each document will at least have the header info? – Sam Marion Oct 04 '16 at 17:37
  • Also, do you think this is a good way to go about this problem or am I just shoehorning it so I can practice using interfaces? – Sam Marion Oct 04 '16 at 17:38
  • The advantage of using the interface is that from the point of view of the process that is interested in getting some CSV data, it doesn't need to care about the "real" type of thing. So you can toss it a huge bucket full of `IDocument`s and say "hey, turn these into CSVs" and it can get on with doing that process. In this case though a base class or abstract class can do the same thing, but personally I prefer Interfaces :) – Stephen Byrne Oct 04 '16 at 17:52
  • Ok thanks so much for your help. I think I'm getting too caught up on trying to do it 'the perfect way'. This is just something I'm doing to learn not really a work assignment so as long as I'm improving as a programmer I'm happy :) – Sam Marion Oct 04 '16 at 17:57