Наследование полезно для делегирования сообщений (вызовов методов) суперклассу с небольшими изменениями. Вы не делегируете сообщения родительскому классу, а изменяете параметр конструктора. Таким образом, нет необходимости наследовать что-либо.
Это нормально:
public class Service{
private Dao dao;
public Service(Dao dao){
this.dao = dao;
}
public void mainMethod(){
dao.step1();
subMethod();
dao.step2();
}
public void subMethod(){
//...
}
}
Затем вы можете создавать экземпляры типа
Service posgresService = new Service(new PosgresDao());
Service redisService = new Service(new RedisDao());
Подробнее см. Внедрение зависимостей