Необязательная реализация метода в абстрактном классе

Я работаю над некоторой структурой, и у меня есть абстрактный класс, который должен быть реализован.

теперь у меня есть некоторые другие вещи, которые пользователь должен иметь возможность настроить, но это необязательно.

Итак, вместо абстрактного метода:

public abstract class AbstractModule {
    public void doSomething() {
        if (logMessage() != null)
            System.out.println(logMessage());
        doStuff();
    }

    protected abstract String logMessage(); // I'm optional
    protected abstract void doStuff();
}

Я думал просто проверить реализацию интерфейса:

public interface Log {
    String logMessage();
}

public abstract class AbstractModule {
    public void doSomething() {
        if (this instanceof Log) {
            if (((Log) this).logMessage() != null)
                System.out.println(((Log) this).logMessage());
        }
        doStuff();
    }

    protected abstract void doStuff();
}

Итак, если кто-то реализует AbstractModule с журналом интерфейса, он также покажет сообщение. Преимущество для implementer, который я вижу: ей не нужно заботиться о реализации logMessage(), как это было бы в первом примере. Является ли такой подход правильным или его следует применять по-другому?

спасибо заранее!

С наилучшими пожеланиями

3 ответов


Я бы сделал регистратор компонентом вашего модуля и определил регистратор no-op по умолчанию в абстрактном классе. Таким образом вы избавитесь от instanceof и все еще сохраняют гибкость.

interface Log {
    void logMessage();
}

public abstract class AbstractModule {
    protected Log log;

    public AbstractModule(){
        this.log = () -> {};
    }

    public AbstractModule(Log log){
        this.log = log;
    }

    public void doSomething() {        
        log.logMessage();        
        doStuff();
    }

    protected abstract void doStuff();
}

вот пример расширения класса AbstractModule:

public class Module extends AbstractModule{

    public Module(){
        super(() -> System.out.println("message"));         
    }

    @Override
    protected void doStuff() {
        // do stuff     
    }   

}

вы можете определить getter-метод для регистратора в абстрактном классе, если вы хотите выставить регистратор:

public Log getLogger(){
    return log;
}

так как этот вопрос был помечен как java-8, альтернативным решением было бы использовать interface и default методы :

public interface Module {
    public default void doSomething() {
        if (logMessage() != null)
            System.out.println(logMessage());
        doStuff();
    }

    public default String logMessage() { return null; } // I'm optional
    public void doStuff();
}

использование :

class NoLogModule implements Module {
    @Override
    public void doStuff() { }
}

class LogModule implements Module {

    @Override
    public void doStuff() { }

    @Override
    public String logMessage() { return "Message "; }

}

одно из преимуществ использования этого подхода вместо Аннотация class заключается в том, что ваш класс теперь свободен для расширения из другого класса. Одним из недостатков этого подхода является то, что вы ничего не можете сделать, чтобы остановить кого-то из переопределение на doSomething способ (Основываясь на вашем коде, не похоже, что вы заботитесь об этом)


потянувшись к instanceof это немного запах кода; обычно есть лучший способ сделать это.

мой первый инстинкт-иметь метод no-op в базовом классе:

class AbstractModule {
  final void doSomething() {
    maybeLogMessage();
  }

  void maybeLogMessage() {}
}

, который, очевидно, ничего не делает; но затем вы можете переопределить в подклассе:

class Subclass extends AbstractModule {
  @Override void maybeLogMessage() {
    System.out.println("The message");
  }
}

что бы напечатать сообщение.

если вы не хотите повторить System.out.println во всех подклассах, вы можете вернуть значение из метода, чтобы указать, что он не должен быть отяжелевший. Например, вы можете потребовать, чтобы строка была непустой:

class AbstractModule {
  final void doSomething() {
    String message = logMessage();
    if (!message.isEmpty()) { System.out.println(message); }
  }

  String logMessage() { return ""; }
}

class Subclass extends AbstractModule {
  @Override String logMessage() {
    return "The message";
  }
}