Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Which design pattern is recommended when implementations only differ in a single method?

I have an interface with 6 methods used to manage datasets. The only method that differs between implementations is getSerializedVersion() and the constructor that is able to parse the serialization string.

public interface DataSets {
  public void addEntry(...);
  public void  removeEntry(...);
  public void manipulateEntry(...);
  public SomeType getEntry(...);
  public List<SomeType> getAllEntries();
  // This differs:
  public String getSerializedVersion()
}

I can't change the Interface.

My first idea was to generate an abstract class and implement the first five methods. For the concrete implementations (e.g. DataSetsXML, DataSetsYAML, ...) I only have to implement getSerializedVersion() and the constructor that that is able to read the String and initialize the object.

To make it more testable a different design might be better (https://stackoverflow.com/a/7569581) but which one?

Answers might be subjective, but I think there are some general rules or a least (objective) advantages and disadvantages of the different approaches,...

like image 875
Edward Avatar asked Dec 25 '22 07:12

Edward


2 Answers

From what you explain the difference is something that is not related to the behavior of the class but just how it is serialized and unserialized. What I mean is that the DataSetsXML and DataSetsYAML would have the same identical funcionality but they would be serialized into different formats.

This means that there is no benefit in keeping getSerializedVersion() coupled with the DataSets class. You should totally decouple them.

You could have a serialization interface sort of:

interface DataSetsSerializer
{
  public DataSets unserialize(String string);
  public String serialize(DataSets sets);
}

and then take care of differente implementations just in this class, eg:

class YAMLDataSetsSerializer implements DataSetsSerializer
{
  public DataSets unserialize(String string) {
    DataSets sets = new DataSets();
    ...
  }

  public String serialize(DataSets sets) {
    ...
  }
}

By elaborating on JB Nizet comment, if you have to keep a DataSetsSerializer inside a DataSets instance (which IMHO makes no sense since they should be decoupled in any case, as a specific way of serialization shouldn't be bound to the data to be serialized) then the approach would be the following:

class DataSets {
  final private DataSetsSerializer serializer;

  public DataSets(DataSetsSerializer serializer, String data) {
    this.serializer = serializer;
    serializer.unserialize(this, data);
  }

  @Override
  public String getSerializedVersion() {
    return serializer.serialize(this);
  }
}

This requires a slight change in the proposed interface and it's not a clever design but it respects your requirements.

like image 144
Jack Avatar answered Dec 28 '22 07:12

Jack


I think it is reasonable to use an abstract class. You can test the concrete implementations of the abstract class (which indirectly tests the abstract class as well).

like image 20
J Fabian Meier Avatar answered Dec 28 '22 05:12

J Fabian Meier