3

I have the following basic design points:

  • Let's say I need to design 2 entities, ADecoder and BDecoder, which are almost the same, except for few details.

  • Let's say both have a function decode() which of course would be separate in what they do, and there are some other functions as well, with the same characteristic, in that they perform entirely distinct operations based on the decoder type.

  • And of course, there are many functions which have the exactly same functionality i.e. are agnostic to what type of decoder it is.

Now should I design only one class and operate on its instances based on their properties, decide what to do in the similar functions and leave the code for the core functions to be the same (entailing switch/if else etc.) ?

Or should I design an abstract base class, and implement the common functions in it and leave the virtual functions to be decided by the respective child classes ?

Or is there some other method that would be more apt for this kind of problem ?

2
  • 1
    These names are terrible but: new Agnostic( new ADecoder() ) gives you polymorphism through composition and delegation which is more flexible than anything else you mentioned. Apr 3, 2018 at 7:18
  • Write a description of what they do (no code) and pass it to your users. Have them determine how they would use it, that is, its calling sequence. They'll come up with things you haven't thought about and give you a better idea of how to implement them. Apr 3, 2018 at 12:33

4 Answers 4

5

As so often, it depends.

If your two decoders decode the same format with slight variations (example: different versions of the same format, or maybe XML with or without character entities), then it should be one decoder with properties that select between the variants.

If it is two distinct formats, there should probably be two decoders (but not always; Clang has the same parser for C and C++ because the sharing benefits simply outweigh the conceptual drawbacks), and the only question is how you share code that is similar.

One simple option is to extract commonalities into utility classes and functions. For example, if the two formats have the same kinds of tokens, you might want to use a common tokenizer for that.

For even more similar parsers, the template method pattern offers itself. (Note that template here doesn't mean C++ templates.) This can be implemented in C++ either through abstract base classes or using the curiously recurring template pattern (CRTP). This is mostly a trade-off between performance and code size.

5

First, I would create an interface that describes what the classes do:

interface IDecoder
{
    void Decode();
}

Next, your decoder classes should implement that interface based on their specifics.

When it comes to the common methods, there are two approaches. Either create a base class that implements those common methods and inherit it by the decoder classes (vertical coupling), or create a separate class that encapsulates that common logic and inject it in the decoder classes (horizontal coupling).

Personally, I prefer composition to inheritance. The reason is that you can more easily test the code and separate responsibilities that way.

3

In general I would agree to @Vladimir Stokic's answer. You should create an interface that describes the responsilities of decoding. Whether that interface is actually an interface or an abstract class does not really matter as long as the public methods (and properties) are the same.

When it comes to implementing the common functionality, you should probably consider whether that common functionality is part of the decoding, or that it's a different responsibility. In the latter case, it should be delegated to another class, allowing easier testing, reuse and if necessary replacement. If the common methods are just different implementations of the same step in the decoding, then a Template Method-pattern (GoF, as @Sebastian Redl mentioned) would be more appropriate.

The Template Method could look like this (C# sample):

public abstract class Decoder
{
    public void Decode()    // probably another return value and arguments'
    {
        DoStep1();
        DoStep2();
        DoStep3();
    }

    public abstract DoStep1();
    public abstract DoStep2();
    public abstract DoStep3();
}

public class ADecoder: Decoder
{
    public override DoStep1()
    {
        // implementation
    }
    public override DoStep2()
    {
        // implementation
    }
    public override DoStep3()
    {
        // implementation
    }
}

This way it is also easy to recognize any responsibilities that should be moved to a separate class: if one of your steps is not specific to decoding, you should refactor it to another class.

2

At some point I would definetly go with abstraction. Your decoding mechanism does not necessarily need to know(at some point it should not know) who does the decoding. The owner module/class/method does only know when the decoding should be done.

void doSomeDecoding(BaseDecoder* decoder){
    ...// do some ops before
    AbstractResult res = decoder->decode();
    ...// do some other stuff with the abstract result.
}

If your decoding type is somewhat the same on all BaseDecoder child classes that you might want to go with Generics(templates in C++ terms). This is especially good when result instance is important.

template<class DecoderType,class ResultType>
ResultType decode(DecoderType a){
   return a.decode();
}

void doDecodingA(ADecoder a){
   AResult result = decode<ADecoder,AResult>(a);
}

void doDecodingB(BDecoder b){
   BResult result = decode<BDecoder,BResult>(b);
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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