mercredi 23 février 2022

Is there an alternative to Template Method pattern when using common logic?

I have a problem. Most of the solutions in the project follow the Template Method design pattern. At the same time, along with the complexity of business logic, solutions are becoming more and more confusing.

For example, there is some Copier interface:

public interface Copier {

    void copy() {
        copyA();
        copyB();
    }
}

Some classes can implement this interface by this way:

public class MusicCopier implements Copier {

    @Override
    protected void copyA() {
        SomeUtils.copy(new File(...), new File(...)));
    }

    @Override
    protected void copyB() {
        // three arguments here
        SomeUtils.copy(new File(...), new File(...)), new File(...));
    }
}

public class DocumentsCopier implements Copier {

    @Override
    protected void copyA() {
        SomeUtils.copy(new File(...), new File(...)));
    }

    @Override
    protected void copyB() {
        // three arguments here
        SomeUtils.copy(new File(...), new File(...), new File(...)));
    }
}

Then somewhere these classes are used like this:

Copier musicCopier = new MusicCopier();
Copier documentsCopier = new DocumentsCopier();

musicCopier.copy();
documentsCopier.copy();

Etc. Great. Here comes the idea to replace the interface with an abstract class and bring the main logic there. Then the abstract class will look like this:

public abstract class Copier {

    protected void copy() {
        copyA();
        copyB();
    }
    
    protected void copyA() {
       SomeUtils.copy(getASource(), getADest()));
    }

    protected void copyB() {
       SomeUtils.copy(getBSource(), getBDest(), getBExcluded()));
    }
    
    // and these terrible methods overridden in superclasses:
    protected void getASource() {}
    protected void getBSource() {}
    protected void getADest() {}
    protected void getBDest() {}
    protected void getBExcluded() {}
    // protected void getOrSetWTF() {}
}

Accordingly, the subclasses will be as follows:

public class MusicCopier extends Copier {

    @Override
    protected void getASource() { /* pass some path here */}
    
    @Override
    protected void getBSource() {/* pass some path here */}
    
    @Override
    protected void getADest() {/* pass some path here */}
    
    @Override
    protected void getBDest() {/* pass some path here */}
    
    @Override
    protected void getBExcluded() {/* pass some path here */}
}

Etc. It turns out that we are "setting up logic", which is now all in an abstract class. And the more complicated it is, the more "get" (or "set"?) there are. All this creates confusion and such code is simply unpleasant to write - especially if the project is large and developers have to "configure superclasses".

In some subclasses, I need to do something else in copyA() or in copyB(). Then I have to override these methods entirely:

@Override
protected void copyA() {
    doSomethingHere();
    
    SomeUtils.copy(new File(...), new File(...)));
    
    doSomethingHereTo();
}

Or put these "doSomething" methods also in an abstract class and then override/implement it in subclasses.

Is there patterns or combinations of them that would help get rid of this ugly design?

Aucun commentaire:

Enregistrer un commentaire