Edit: I've realised that this pattern feels a lot like currying, a technique that functional programmers use to specify function parameters in advance of invocation. The difference here is that we're currying constructors on objects instead of simply currying functions.
Throughout a couple of projects I've found myself using this bizarre design pattern that I can't find the name of. Does it have a name? Perhaps it's just bad practice, you tell me.
With this pattern, you would have...
I will define a hypothetical scenario to provide some context.
Bob is writing a small API for scanning source code. He wants to be able to check if a comment starts/ends at a given index within the source code.
Here is Bob's code.
public abstract class CommentDetector {
private final String startPattern;
private final String endPattern;
protected CommentDetector(String startPattern, String endPattern) {
this.startPattern = startPattern;
this.endPattern = endPattern;
}
public boolean commentStartsAt(int index, String sourceCode) {
// ...
}
public boolean commentEndsAt(int index, String sourceCode) {
// ...
}
}
You may be wondering why it is abstract but has no abstract methods. This is simply because Bob doesn't want you to instantiate it directly. Bob wants you to write an implementation of CommentDetector
and then instantiate that instead. Here are two of Bob's implementations...
One for multi-line comments in Java:
public class JavaMultiLineCommentDetector extends CommentDetector {
public JavaMultiLineCommentDetector() {
super("/*", "*/");
}
}
One for single-line comments in Java:
public class JavaSingleLineCommentDetector extends CommentDetector {
public JavaSingleLineCommentDetector() {
super("//", "\n");
}
}
Bob has written these implementations for us so that we can write new JavaMultiLineCommentDetector()
instead of new CommentDetector("/*", "*/")
.
Bob also encourages you to write your own implementations for other languages if need be.
It feels like the intent of this design pattern is to improve readability of the code by pre-defining constructor calls.
This design pattern gives a polymorphic feel to the code (even though it may/may not be truly polymorphic).
Writing new implementations is quick and easy.
Implementations do not depend on each other, and can be compiled/deployed independently.
With the extensible enum pattern (already noted in comments) you can also avoid the inheritance:
public interface CommentDelimiter {
String getStartPattern();
String getEndPattern();
}
public interface CommentDetector {
boolean commentStartsAt(int index, String sourceCode);
boolean commentEndsAt(int index, String sourceCode);
}
public enum CommentDetectors implements CommentDetector {
JAVA_MULTILINE(CommentDelimiters.JAVA_MULTILINE),
JAVA_SINGLELINE(CommentDelimiters.JAVA_SINGLELINE);
// ... store commentDelimiter
public boolean commentStartsAt(int index, String sourceCode) {
// ... using commentDelimiter.getStartPattern()
}
public boolean commentEndsAt(int index, String sourceCode) {
// ... using commentDelimiter.getEndPattern()
}
}
public enum CommentDelimiters implements CommentDelimiter {
JAVA_MULTILINE("/*", "*/"),
JAVA_SINGLELINE("//", "\n");
// ... store start, end
}
It feels like the intent of this design pattern is to improve readability of the code by pre-defining constructor calls.
In OOP, constructors should not be used to define contract since :
Generally, the Factory method or the builder Design Patterns are more commonly used when you want allow to your class clients to choose what object must be used in a processing :
Abstract class :
public abstract class CommentDetector {
private final String startPattern;
private final String endPattern;
public abstract String getStartPattern();
public abstract String getEndPattern();
public boolean commentStartsAt(int index, String sourceCode){
getStartPattern()...
}
public boolean commentEndsAt(int index, String sourceCode){
getEndPattern()....
}
Concrete class
public class JavaSingleLineCommentDetector extends CommentDetector {
public String getStartPattern(){
return "//";
}
public abstract String getEndPattern(){
return "\n";
}
}
This design pattern gives a polymorphic feel to the code (even though it may/may not be truly polymorphic).
Writing new implementations is quick and easy.
It's true in this case since the class becomes extensible. You can use a concrete class and override any factory method if you want.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With