Reputation:
I'm working on some framework and I got an abstract class, which should be implemented.
Now I got some other stuff the user should be able to configure, but it is optional.
So instead of the abstract method:
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();
}
I thought of just checking for an interface implementation:
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();
}
So, if someone is implementing AbstractModule with the interface Log it would also show the message. The benefit for the implementer that I see: She doesn't need to care about implementing logMessage(), as it would be in the first example. Is this a valid approach or should it be done differently?
Thanks in advance!
Best regards
Upvotes: 13
Views: 11130
Reputation: 15212
Since this question has been tagged as java-8
, an alternate solution would be to use an interface
and default
methods :
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();
}
Usage :
class NoLogModule implements Module {
@Override
public void doStuff() { }
}
class LogModule implements Module {
@Override
public void doStuff() { }
@Override
public String logMessage() { return "Message "; }
}
One advantage of using this approach instead of an Abstract class is that your class is now free to extend from another class. One disadvantage of this approach is that there is nothing you can do to stop someone from overriding the doSomething
method (Based on your code, it doesn't look like you care about this)
Upvotes: 5
Reputation: 2789
I would make the Logger a component of your module and define a default no-op logger in the abstract class. This way you get rid of the instanceof
and still preserve the flexibility.
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();
}
Here is an example class extending AbstractModule
:
public class Module extends AbstractModule{
public Module(){
super(() -> System.out.println("message"));
}
@Override
protected void doStuff() {
// do stuff
}
}
You can define a getter-method for the logger in the abstract class if you want to expose the logger:
public Log getLogger(){
return log;
}
Upvotes: 5
Reputation: 140319
Reaching for instanceof
is a bit of a code smell; there's normally a better way of doing it.
My first instinct is to have a no-op method in the base class:
class AbstractModule {
final void doSomething() {
maybeLogMessage();
}
void maybeLogMessage() {}
}
which obviously does nothing; but then you can override in a subclass:
class Subclass extends AbstractModule {
@Override void maybeLogMessage() {
System.out.println("The message");
}
}
which would print the message.
If you don't want to repeat the System.out.println
in all subclasses, you can return a special value from the method to indicate that it shouldn't be logged. For example, you can require the string to be non-empty:
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";
}
}
Upvotes: 2