Sunday, August 9, 2009

Case Study: Console Application

In this case study the goal is to design an application that access to file in order to get data, process it and print the result to file.

Solution without design:

For this first solution the design is ignored and only one class named CDataProcessor is used to:

- Get data from file.
- Processing data.
- Print result.

And the main method invokes the methods of this class.



drawback of this solution:

Low cohesion: CDataProcessor class has many responsibilities so we can’t easily reuse algorithm in other application.
High coupling: the processing is high coupled with console and also with data provider.

Design refactoring:

- High Cohesion:
To improve cohesion each responsibility must be assigned to a different class so we need three classes:

CFileProvider: To get data from File.
CDataProcessing: To process data, and this class can use other classes to complete processing, but to simplify the design we consider that's sufficient for our processing.
CResultReporting: To report result to file.

So each class has its own responsibility, there are advantages of this design:

- Easy to understand classes.
- Easy to maintain.
- Easy to reuse classes in other applications.

- Low coupling:
What happen if data exist in database not in file, in our last design our application is high coupled with file provider.

To resolve this problem we need an interface that provide methods to get data from anywhere , and for the case of file we need class that implement this interface.

For that using NVI can be good solution, so we need an interface IDataProvider




And CFileProvider inherit from IDataProvider to implement GetDataFromImpl, and the same design can be used for CDataProcessing and CReportResult.

Here’s the new collaboration between classes after refactoring:


- Class Factory:
In the latest design the creation of concrete instances of IDataProvider,IDataProcessing and IReportResult are created by Main method.

A better approach is to assign this responsibility to a factory class so a logic to instanciate a family
of instances needed is isolated.

- Controller:
The orchestration between all classes is implemented in the main method.
it's better to assign this responsability to a Controller class, so we can use it in other applications.

The controller needs three classes to interact with them, so the question is how we bind instances to controller?

Two solutions can be used:

- Add method named BindInstances(IDataProviderPtr,IDataProcesingPtr,IReportResultPtr).

- Use template so the controller will be instantiated like that:
CController<CFileProvider,CDataProcessor,CConsoleReport>

The difference between two solutions is that for the first one each DataProvider must inherit from IDataProvider, and for the second one CFileProvider need only the method GetData (Data&) even if it’s inherit from another class than IDataProvider.

Here’s the new collaboration between classes after refactoring:

Benefit of refactoring:

After refactoring this application became more flexible and we can use it for different scenario:
- Can get data from file,database,xml file,csv file,…
- Can process with many class not only one.
- Can report to console,file,….

Which libraries to use for this application?

In our application we need read/write to file, processing and write to console, in this case STL and Boost are in general sufficient.