The way to design structure of classes

Asked by At

I have very simple structure of classes. Class B and C inheriting from A. The functionality is similar in the case of some functions and properties. Class B and C has different processing of the data but the classes have the same output (the function creating the output is inside the A class). Example of the structure: enter image description here

Now I need to extend the functionality. I need to add the option for a little differences in processing and the outputting of the data (class X). This option is managed by configuration file so I want keep the old way of the downloading, processing and outputting the data, e.g.:

  1. option 1 - download the data without threads using the old way processing and output
  2. option 2 - download the data without threads using the new way processing and output
  3. option 3 - download the data with threads using the old way processing and output
  4. option 4 - download the data without threads using the new way processing and output

I am not sure how to implement combination of the new processing and outputting the data. I need combination of Class B and X (BX) and Class C and X (CX). I think about these options:

  1. The easiest way but the worst - duplicating some functions in the class B and class A.
  2. Keep the classes A and B and add the combination of BX and AX classes.
  3. Rewrite the classes A and B only for downloading the data. Then add classes for processing the data and then add classes for outputting the data. All classes will share the objects. Looks like the best option but with the most work to do.

Is there any better option (e.g. design pattern or something like this) how to extend the classes with the cleanest way?

2 Answers

3
Community On Best Solutions

It seems to me that you have a Dataflow.

What you have is Get (Download) Data -> Process Data -> Output Data. This basically is a pipeline with three stages.

If you are using object to models this you, have two types of objects: the ones who perform the operations and one or more that manage the data flow and the ordering of operations.

Lets use three interfaces (you may use abstract classes to define them it doesn't matter) to define the steps of the pipeline: IDataDownloader, IDataProcessor and IDataOutputer.

Lets add another object that will represent and manage the pipeline:

(Note: I don't have experience with python, so I will write it in C# sorry..)

public class Pipeline {

  private IDataDownloader mDataDownloader;
  private IDataProcessor mDataProcessor;
  private IDataOutputer mDataOutputer;

  public void Setup() {

    // use a config file to select between the two ways

    if (Config.UseOldWayDownloader) {
      mDataDownloader = new OldWayDownloader();
    }
    else {
      mDataDownloader = new NewWayDownloader();
    }
    // ....

    // or you can use service locator or dependecy injection that will get
    // a specific downloader based on configuration/setup

    mDataDownloader = Services.Get<IDataDownloader>();
    // ....
  }

  public void Execute() {

    var data = mDownloader.Download();

    var processedData = mDataProcessor.Process(data);

    mDataOutputer.Output(processedData);
  }
}

This way you get nice separation of concerns and you get a modular system that you can extend. This sytem is also composable. You can compose it's parts in different ways.

It may seem like more work, but this may not be true. Clean designs are often easier to write and extend.

It's true that you may write little more code, but this code can be writen faster because of the clean design and you can save time from debugging. Debugging is the thing that comsumes most of the time we write software but it's often overloked by programmers that thinks that they can measure only by writing and code lines.

One example for this is the case with loose typed languages. Quite often you write less code so it's fater, but it's prone to errors due to mistakes. In case of error you get more (and sometimes harder) debugging so you waste time.

If you start with a simple proof of concept app they will significantly speed up developement. Once you get to the point of acqually having a robust software that is tested then a different forces kick in.

In order to ensure that your software is robust you have to write more tests and more checks/assertions to make sure that everything runs smoothly. So in the end you will probably have the same amount of code as strong typing does some of the checks for you and you can write less tests.

So what's better? Well... it depends.

There are several factors that will influence the speed of developement. The taste and experice of the programmers with a language. The application will also affect this. Some applications are easier to write on loose typed languages, other on strong typed languages.

Speed can be different, but it's not always like this, sometimes it's just the same.

I think that in your case you will waste more time trying to get the hierarchy right and debugging it even if in the end you have few lines less code. In the long run, having a design that is harder to understand will slow you down significantly if you need to extend it.

In the pipeline approach adding new ways of downloading, processing or outputting the data is a matter of adding a single class.

2
Thomas Kilian On

Well, to make it an UML answer I add the class diagram:

enter image description here

The Pipeline has those three processing classes referred. They are all abstract, so you will need to implement them differently. And the configuration will assign them according to what the configuration (file) says).