public class AssignResponseImpl extends ServletSupport {
public AssignResponse assign(Ident ident, int low) {
try {
return getService().assign(ident, low);
} catch (Exception e) {
logger.debug("caught Exception", e);
return new AssignResponse("ERROR", e.getMessage());
}
}
}
The reason I don't like them is you can't factor them. And because you can't factor them, it's difficult to create (and maintain) standard behaviour.
Like always logging the error. And always returning the exception message in the correct place. Let's have a look at another:
public SearchResponse search(Ident ident, Criteria criteria) {
try {
return getService().search(ident, criteria);
} catch (Exception e) {
logger.debug(e);
return new SearchResponse("ERROR", e.getMessage());
}
}
same error handling.
Now, SearchResponse and AssignResponse share a common superclass, Response. So in the case of an exception, only the class differs, the fields
we're filling in are those in Response. This doesn't really help in Java, we have to cut and paste the try catch, with only the name of the class changing.
We also have to add
in the extra constructor into the subclasses, and all they do is fill in the fields in the superclass. But in Scala, we can use
two tricks: manifests and first class functions.
Manifests allow you access to information about classes which you wouldn't normally have available in Java, in this case the
return type of the service method (A):
abstract class ServletSupport[A <: Response] {
protected def exception(fn: => A)(implicit m: Manifest[A]): A = {
try {
fn
} catch {
case e => {
logger.debug("caught Exception", e)
// create new instance of A
val t = m.erasure.newInstance().asInstanceOf[A]
t.setResponseCode("ERROR")
t.setMessage(e.getLocalizedMessage())
t
}
}
}
}
A, the return type of our target method), and an implicit Manifest parameter. Importantly, this is added by the Scala compiler, so we don't have to add
the parameter manually.
In this case, we're asking for extra information about A.
Our exception method calls the passed in function, and if there isn't an exception thrown, then it returns the value returned by the function. If there is
an exception, then the return value is a new instance of A, with the response code and message filled in. We know that we can call setResponseCode and setMessage on an A
because in the class definition, we're setting the type bounds of A, it has to extend Response. OK, so what is our calling code like now?
class AssignResponseImpl extends ServletSupport[AssignResponse] {
def assign(ident: Ident, low: Int) = exception {
getService().assign(ident, low)
}
}
this means it looks like it's part of the language. And this is all type safe, I only have to specify the name of the response class once in the definition.