3

The problem is the following: I have to download a set of JSON files and convert them to a certain format. There are 5 output formats (Let's call them A, B, C, D, E) and all of the downloaded json files are going to be in of of two formats (call them J1, and J2).

So once I decide whether I am downloading J1 or J2 type of files, I should be able to convert them into one of the 5 formats. Basically this means that there are potentially 10 different conversions possible.

I was wondering how to better design this application the current setup is the following:

  1. Converter class - which implements common behaviors.
  2. An interface which all strategies should have
  3. 10 different strategies. from J1 to (A,B,C,D,E) and J2 to (A,B,C,D,E)

All 10 different strategies implement the interface of converting to necessary format, a Converter object contains a strategy which is set at run time when I understand exactly which one is required (based on user input)

This seems unbelievably sloppy, maybe I am not understanding the Design Pattern as necessary. If I have all the strategies implemented in different classes, they all have a convert function, why should I bother to create a Converter object and initialize it with my chosen strategy, when I could simply make a ConcreteStrategy type of object and call the convert function on them.

3 Answers3

1

I would solve this problem not with strategy but visitor pattern. I would structure the data formats J1 and J2 in a hierarchy as:

public abstract class JsonFormat {
  public string Text {get; set;}
  public abstract void Accept(IVisitor visitor);
}

public class J1 : JsonFormat {
  public override void Accept(IVisitor visitor) {
    visitor.Visit(this);
  }
}

public class J2 : JsonFormat {
  public override void Accept(IVisitor visitor) {
    visitor.Visit(this);
  }
}

Now, we will have a visitor for each possible conversion format. Basically, the visitor is the converter.

public interface IVisitor {
  void Visit(J1 firstFormat);
  void Visit(J2 secondFormat);
}

public class AFormatConverter : IVisitor {
  public string J1Conversion {get;}
  public string J2Conversion {get;}

  public void Visit(J1 firstFormat) {
    //implement the logic of conversion of J1 to A here
    //set J1Conversion string to the conversion after logic
  }

  public void Visit(J2 secondFormat) {
    //implement the logic of conversion of J2 to A here
    //set J2Conversion string to the conversion after logic
  }
}

In the same manner you can write Visitors for each possible conversion format like B,C,D,E. With Visitor pattern you will have only 5 converters. This is one way of solving problem and I agree that Visitor pattern is not easy to understand. Also, there are downsides and upsides of every solution.

0

I think you are over-thinking it. Depending on your language's level of support for generics and/or lambdas, an implementation could look like this:

using System;

public interface IConverter<T1, T2>
{
    T2 Convert(T1 source);
}

public class StringStringConverter: IConverter<string, string>
{
    public string Convert(string source)
    {
        return source.ToLower();
    }
}

public static class Stackoverflow
{
    public static T2 Convert<T1, T2>(T1 source, IConverter<T1, T2> converter)
    {
        return converter.Convert(source);
    }
}

public class Program
{
    public static void Main()
    {
        // in C#, can use reflection to find class which implements the desired converter
        var func = new StringStringConverter();
        var input = "Hello";
        var result = Stackoverflow.Convert<string, string>(input, func);
        Console.WriteLine(result);
    }
}

Now we have removed the decision which converter to pick and how to instantiate it from the actual conversion.

Martin K
  • 2,947
0

The point of the strategy pattern is to isolate the choice of what to do from the doing of it. Let's say you have a UI with a pair of dropdowns, InputType and OutputType, and a button DoConversion.

You can have UI code like

partial class UI {   
    public Converter { get; set; }

    void onInputTypeCombo_Changed(InputType inputType) {
        Converter.setInputStrategy(inputType);
    }

    void onOutputTypeCombo_Changed(OutputType outputType) {
        Converter.setOutputStrategy(outputType);
    }

    void onDoConversion_Clicked() {
        Converter.doConversion();
    }
};

And the Converter will instantiate concrete strategy objects based on the passed InputType and OutputType. But it won't do the conversion until the button is pressed.

Caleth
  • 12,190