Java design pattern for two classes sharing identical and similar but different methods

Rob*_*uch 5 java oop design-patterns

Let's say my application has some services implemented as ClassA and ClassB. Both have some similarities but also differences.

  1. Both classes have a start() method with the same method signature but a different implementation.
  2. Both classes have a process() method with a different signature and a different implementation.
  3. Both classes have an identical log() method, i.e. the code is exactly the same.

Class A

public class ClassA {

    public String start(String s1, String s2) {
        startImplementation();
        return someString;
    }

    public String process(String s) {
        processingImplementation();
        return processedString;
    }

    private String log(String s) {
        logImplementation();
        return sharedString;
    }
}
Run Code Online (Sandbox Code Playgroud)

Class B

public class ClassB {

    public String start(String s1, String s2) {
        otherStartImplementation();
        return someString;
    }

    public String process(Long l) {
        otherProcessingImplementation();
        return processedString;
    }


    private String log(String s) {
        logImplementation();
        return sharedString;
    }
}
Run Code Online (Sandbox Code Playgroud)

I'm having trouble thinking of a "design pattern" how I could organize this in a more generic way. As of 3. I could easily move this method to a superclass which ClassA and ClassB extend. But how would/could I design the application so that 1. and 2. are also taken into account?

Item 1. sounds a little bit like an interface to me but I don't have any idea how this could be combined with the superclass for item 3. And what about item 2?

kay*_*ya3 5

I would design this so that class A and class B extend a generic abstract class, with a type parameter for the process method's parameter type.

public abstract class BaseClass<T> {
    public abstract String start(String s1, String s2);

    public abstract String process(T value);

    protected final String log(String s) {
        // shared log implementation
    }
}
Run Code Online (Sandbox Code Playgroud)
public class A extends BaseClass<String> {
    @Override
    public String start(String s1, String s2) {
        // A.start implementation
    }

    @Override
    public String process(String s) {
        // A.process implementation
    }
}
Run Code Online (Sandbox Code Playgroud)
public class B extends BaseClass<Long> {
    @Override
    public String start(String s1, String s2) {
        // B.start implementation
    }

    @Override
    public String process(Long l) {
        // B.process implementation
    }
}
Run Code Online (Sandbox Code Playgroud)

In Java 9+, you could instead use a generic public interface Base<T> instead of an abstract class, by giving log a default implementation. However, that doesn't allow you to make log only accessible to the implementing classes, and it doesn't prevent subclasses from overriding log.