2

I am looking for the best approach to design the following system :

  • I have a class of object, called Compilers (C, Go..Etc)
  • I have another class of object, called Protocols (HTTP, RPC, WEBSOCKET..Etc)
  • I have another class of object, called Serialization (BINARY, JSON..Etc)

Now each compiler has to be matched with the right implementation of the protocol and the serialization.

For instance, if the compiler is C, then it should be matched with CProtocolHTTP or CProtocolRPC w.r.t the protocol and CSerializationBinary or CSerializationJson w.r.t Serialization, if the compiler is Go it should be matched with GoProtocolHTTP or GoProtocolRPC w.r.t the protocol and GoSerializationBinary or GoSerializationJson w.r.t Serialization.

The client should have the choice to choose any combination :

  • Go with HTTP and BINARY
  • Go with HTTP and JSON
  • Go with RPC and BINARY
  • C with HTTP and JSON ...etc

I tried Factory and Strategy but I was unable to make them work in a neat way.

What would be the right design patterns to use in order to implement this in Java ?

  • I would consider bridge – gnat Aug 6 '18 at 9:49
  • What do you mean by "matching" ? Like string matching ? – Spotted Aug 6 '18 at 11:08
  • I want to be able to have a method like that : getCompiler("Go","HTTP","BINARY") or getCompiler("Go","RPC","BINARY") and get the compiler in return already configured behind the scene. – devPsycho Aug 6 '18 at 11:12
  • Do you really need to parse strings or you could also work with enums ? – Spotted Aug 6 '18 at 11:56
  • @Spotted Enum are okay too. – devPsycho Aug 6 '18 at 12:38
4

I'm note sure you really need any specific design pattern to solve your problem. I think using dependency injection should be sufficient.

The most important thing is to require both a protocol and a serialization mechanism to be provided when creating a compiler. This can be enforced in the compiler's constructor.

First declare all the supported protocols under a common interface:

public interface Protocol
{
    //...
}
public final class Http implements Protocol
{
    //...
}
...

The same for serialization

public interface Serialization
{
    //...
}
public final class Binary implements Serialization
{
   //...
}
...

Then each possible compiler can be declared as follow

public interface Compiler
{
    //...
}
public final class Go implements Compiler
{
    private final Protocol protocol;
    private final Serialization serialization;
    public GoCompiler(Protocol protocol, Serialization serialization)
    {
        this.protocol = protocol;
        this.serialization = serialization;
    }
    //...
}
...

Final usage will looks like this:

Compiler c1 = new Go(new Http(), new Binary());
Compiler c2 = new Go(new Http(), new Json());
Compiler c3 = new Go(new Rpc(), new Binary());
Compiler c4 = new C(new Http(), new Binary());
  • There a Serialization implementation for each compiler. For instance, for Go there is a GoBinarySerialization or GoJSONSerialization, for C there is a CBinarySerialization or CJSONSerialization. How could this be possible with this implementation. – devPsycho Oct 10 '18 at 21:26
  • @devPsycho each of these classes have to implement a common interface (in my example, I named it Serialization) so that you are able to construct instances of compiler in a composable manner. – Spotted Oct 11 '18 at 5:19
  • If I do that Compiler c1 = new Go(new Http(), new Binary());, how it can find the right Binary implementation (in this case for Go) ? – devPsycho Oct 11 '18 at 23:49
  • @devPsycho It can't. I assumed you had only 1 "binary" implementation which was not tied to a language. You have to do Compiler c1 = new Go(new Http(), new GoBinarySerialization()); – Spotted Oct 12 '18 at 9:32
  • It is because of that complexity that I thought that we need a design pattern. – devPsycho Oct 14 '18 at 5:49
3

If the Protocol and Serialization classes are independent of each other, but both depend on the Compiler, then this situation fits the Abstract Factory pattern.

In essence, you would have two factory classes for each Compiler to create the correct Protocol instances and one to create the Serialization instances.

In pseudo-code, the class structure could look something like this.

interface IProtocolFactory {
  IProtocol createHttp()
  IProtocol createRpc()
  ...
}

interface ISerializationFactory {
  ISerializer createJson()
  ...
}

interface IFactoryFactory {
  IProtocolFactory getProtocolFactory()
  ISerializationFactory getSerializationFactory()

  static IFactoryFactory getFactory(Compiler x)
  {
    if x == C then return new CObjectsFactory()
    if x == Go then return new GoObjectsFactory()
    ...
  }
}

class CObjectsFactory implements IFactoryFactory {
  IProtocolFactory getProtocolFactory() 
  { 
    return new CProtocolFactory() 
  }
  ISerializationFactory getSerializationFactory()
  { 
    return new CSerializationFactory() 
  }
}

class CProtocolFactory implements IProtocolFactory {
  IProtocol createHttp()
  { 
    return new CProtocolHTTP() 
  }
}
  • How can I use that to create a compiler from the client. I want to have something like that on the client : getCompiler("Go","HTTP","BINARY") or getCompiler("Go","RPC","BINARY") – devPsycho Aug 6 '18 at 11:18
  • 1
    @devPsycho: That is a completely different question than what I understood. I understood the question as "I have an object of a Compilers subclass and I want to have matching objects of Protocol and Serialization classes". Can you update your question to make it more clear what you are trying to achieve? – Bart van Ingen Schenau Aug 6 '18 at 13:44

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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