Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

What is the common practice to write classes which take some data class and store it to some backend? In my case I have table classes (columns, headers, etc.) which are written to Excel. So I have a class Table with the data and a class ExcelDocument with access operations and basic writing (cells, ...).

Where would I put the code which knows how to deal with Table data and knows how to write that the ExcelDocument? Both might be subject to refactoring at some point.

Do I use Table.write_to_excel(excel) or ExcelDocument.write_table(table) or even some need intermediate class? With the concept "a class should serve just one purpose" I'd most likely write an intermediate class?

share|improve this question

3 Answers 3

up vote 2 down vote accepted

I would consider modeling your output format as a separate class. In other words, go with your ExcelDocument idea. This lends itself well to the something like the Strategy pattern if you need to support multiple back end formats, now or in the future.

You would have an interface that defines the operations that you perform on the data store, such as reading and writing. You then implement that interface in a number of subclasses, each designed to handle a specific type of backend, such as CSV files, Excel XLS files, insert-flavor-here SQL database. If you only have one output now, you don't necessarily need the interface, but if you know for sure that you'll be adding more formats in the future, take care of the class hierarchy now.

Of course, it does depend on how complex these actions are. You might favor decomposing into both "reader" and "writer" classes instead of putting everything in one. This might also be more in line with the Single Responsibility Principle.

share|improve this answer
    
So I create a new class TableStorage (any better name)? Then I subclass ExcelTableStorage which keeps an attribute to ExcelDocument in order to write to Excel. I would initialize/bind ExcelTableStorage to that excel doc and call with different table data? Or would you pass table and excel doc anew on each conversion call? What if I have extended table storage (e.g. colored columns) and whenever both the data (table) and the backend (excel) know about it, then they behave accordingly (i.e. color the columns)? I would subclass the interface with a compatible call? –  Gerenuk Mar 22 '12 at 15:16
    
@Gerenuk That all depends on your need and requirements. All of those are options. I would deal with your "extended table store" in the ExcelTableStorage rather than a subclass, though. If the client knows that it's Excel, let them use those additional methods. Otherwise, a client would just have access to the TableStorage interface. –  Thomas Owens Mar 22 '12 at 15:20
    
OK, one last idea. Is it sensible to provide Table.write(table_storage) functions which calls the TableStorage? Or similarly maybe ExcelDocument.get_table_storage()? It can actually be convenient in some cases because I don't have to import the intermediate storage class. But is it recommended? –  Gerenuk Mar 22 '12 at 15:27

You can approach this problem with a dependency injection mindset: The table should expose an interface that it requires to write itself to a persistent store.

You could pass this into the Table constructor:

Table(ITableWriter tableWriter)
{
    _tableWriter = tableWriter;
}

And then Table implements something like:

void Write(string writeID)
{
    //for all rows in the table
    _tableWriter.Write(writeID, rowToWrite);
}

Then your calls could be:

var excelWriter = new ExcelWriter(); // implements ITableWriter
var table = new Table(excelWriter);
table.Write("DocumentName");

The trick is then to define the ExcelWriter class that implements the ITableWriter interface, with the deliverable being an Excel document.

That way, should you need to write the table somewhere else (to another database? to a flat file?) you won't have to change the Table class, just produce another implementation of the ITableWriter interface.

As added bonus, you can mock the ITableWriter interface for separate testing of your Table class.

share|improve this answer
    
Doesn't it pollute Table if I define .Write functions? What does it mean that a "deliverable is an Excel document"? Will it return an ExcelDocument object or does it just internally keep track of it? –  Gerenuk Mar 22 '12 at 15:23
    
Depends on what you mean by "pollute". The idea is to allow Table to define how it is written --- not let someone else define it. If some other class defines how Table is written, then that other class can know too much about the internals of Table. –  Peter K. Mar 22 '12 at 15:43
    
As to your second question: What do you need? Does ExcelWriter actually write the file? Then that's the deliverable. Does ExcelWriter instead create the ExcelDocument, but some other class does the storage? Then the deliverable is the ExcelDocument object. I don't know enough about what you are trying to do to be more explicit, hence the weasel word of "deliverable". –  Peter K. Mar 22 '12 at 15:45
    
In the simplest case, I just want to write a table to an existing excel file (since often I will write multiple table, so I don't want to recreate the excel each time). –  Gerenuk Mar 24 '12 at 7:39

You're right, those classes should serve a single purpose as they represent your model. You should write a service class that transforms one of those classes into the other when necessary.

Structurally, service classes and model classes are the same, but they are there to define the seperate concerns of different tiers of an MVC implementation, for instance. A concrete service class defines the API of an external service, like transforming a data table into an excel spreadsheet, and a model class defines the API of the application's data model.

So, while the base classes may be similar, the concrete classes created by extending these base classes serve two entirely different purposes.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.