WhiningBuilder
From APIDesign
(→Multiple Throws) |
(→Multiple Throws) |
||
Line 7: | Line 7: | ||
<source lang="java"> | <source lang="java"> | ||
public final class Builder<MissingName extends Exception,MissingMIMEType extends Exception> { | public final class Builder<MissingName extends Exception,MissingMIMEType extends Exception> { | ||
- | + | public Source build() throws MissingName, MissingMIMEType { | |
+ | if (this.name == null) { | ||
+ | throw (MissingName)new Exception("name missing"); | ||
+ | } | ||
+ | if (this.mimetype == null) { | ||
+ | throw (MissingMIMEType)new Exception("MIME type missing"); | ||
+ | } | ||
+ | // create and | ||
+ | return source; | ||
+ | } | ||
+ | public static Builder<Exception,Exception> newFromText(String content) { | ||
+ | // create new builder without name and MIME type | ||
+ | return theBuilder; | ||
+ | } | ||
+ | |||
+ | public Builder<RuntimeException,MissingMIMEType> name(String name) { | ||
+ | // specifying name | ||
+ | this.name = name; | ||
+ | // keeps the "mime type" exception unchanged but | ||
+ | // turns the "name" exception into runtime one | ||
+ | return (Builder<RuntimeException,MissingMIMEType>)this; | ||
+ | } | ||
+ | |||
+ | public Builder<MissingName,RuntimeException> mimeType(String mimeType) { | ||
+ | // specifying MIME Type | ||
+ | this.mimeType = mimeType; | ||
+ | // keeps the "name" exception unchanged but | ||
+ | // turns the "mime type" exception into runtime one | ||
+ | return (Builder<MissingName,RuntimeException>)this; | ||
+ | } | ||
} | } | ||
+ | |||
+ | Please note that ''MissingName'' and ''MissingMIMEType'' are generics type parameters - e.g. they aren't real exceptions, just specifications/place holders for the actual {{JDK|java/lang|Exception}} subclass. This gives us early compile time warning as in [[ChameleonBuilder]]. When one tries to compile: | ||
+ | |||
+ | <source lang="java"> | ||
+ | private static Source buildMySource() { | ||
+ | return Source.newFromText("function hi() { print('Hi'; }"). | ||
+ | build(); | ||
+ | } | ||
+ | </source> | ||
+ | |||
+ | one gets a compilation error, as the build method throws a [[checked exception]]. Even if one specifies '''name''', the error still remains. Only if both '''name''' and '''mimeType''' are specified the [[checked exception]]s go away (as they are replaced by runtime [[exception]]s). | ||
+ | |||
+ | === Explaining Why === | ||
+ | |||
+ | === Drawbacks === | ||
+ | |||
+ | === Evolution === | ||
[[TBD]] | [[TBD]] |
Revision as of 13:31, 16 June 2016
One problem with ChameleonBuilder is that the change of the return type works only for a single essential attribute. Can we modify the [[builder] pattern to work with multiple such attributes? Another problem is the reported error - it doesn't push the user towards proper fix. Can we improve that and encourage cluelessness in users of our builders?
Contents |
Multiple Throws
Java allows only a single return value - yet, it allows multiple throws - and throwing an exception is kind a return value as well. As the BuilderWithConditionalException it is possible use a generic type parameter to control whether the exception thrown from the build() method is checked exception or unchecked one. Let's do the same with multiple throws for two essential attributes (name and mimeType):
public final class Builder<MissingName extends Exception,MissingMIMEType extends Exception> { public Source build() throws MissingName, MissingMIMEType { if (this.name == null) { throw (MissingName)new Exception("name missing"); } if (this.mimetype == null) { throw (MissingMIMEType)new Exception("MIME type missing"); } // create and return source; } public static Builder<Exception,Exception> newFromText(String content) { // create new builder without name and MIME type return theBuilder; } public Builder<RuntimeException,MissingMIMEType> name(String name) { // specifying name this.name = name; // keeps the "mime type" exception unchanged but // turns the "name" exception into runtime one return (Builder<RuntimeException,MissingMIMEType>)this; } public Builder<MissingName,RuntimeException> mimeType(String mimeType) { // specifying MIME Type this.mimeType = mimeType; // keeps the "name" exception unchanged but // turns the "mime type" exception into runtime one return (Builder<MissingName,RuntimeException>)this; } } Please note that ''MissingName'' and ''MissingMIMEType'' are generics type parameters - e.g. they aren't real exceptions, just specifications/place holders for the actual {{JDK|java/lang|Exception}} subclass. This gives us early compile time warning as in [[ChameleonBuilder]]. When one tries to compile: <source lang="java"> private static Source buildMySource() { return Source.newFromText("function hi() { print('Hi'; }"). build(); }
one gets a compilation error, as the build method throws a checked exception. Even if one specifies name, the error still remains. Only if both name and mimeType are specified the checked exceptions go away (as they are replaced by runtime exceptions).