When the calling code can make a recovery from the failure or take a
different course of action, it may be better to use a checked exception.
My example should have had the DuplicateNameException extend Exception,
not RuntimeException. This would have made it part of the contract of
the API. This avoids the leaky abstraction code smell. The downside to
this is if the client could make a recovery, but doesn't care to. This
will lead to the client code just re-throwing the Exception or
swallowing it up by catching it and just logging it. I see this with
SQLExceptions all the time. This makes me re-think my first statement
and go with making it extend RuntimeException. This allows it to bubble
up and be logged, hopefully just in one spot.
So, to sum it up a little differently, if the client code can make a
recovery, create a custom exception, nicely named, that extends
RuntimeException. If the client can not recover, then wrap the
implementation specific exception in a RuntimeException or just throw
the most appropriate JDK exception that is a subclass of
RuntimeException. Why should the client code care if there is a
exception it can not recover from. In this case, the client code
shouldn't have to deal with the exception. On the outside chance the
API developer guesses wrong and the client does care about the root
cause, they can still get it from the RuntimeException. This isn't
ideal, but I think it is better than seeing a lot of empty catch clauses
or catch clauses that just re-wrap an Exception to a RuntimeException
and re-throws it.
Maybe Helper was a bad label. I guess my point wasn't very good. I was
just thinking that if you are going to create a new Type, i.e. a custom
exception, it is still a Type. There may be an opportunity where it
makes sense to add functionality to it. My example was just very bad,
the functionality provide by the DuplicateNameException belongs in a
domain object.
I also think having just one return per method makes code easier to
understand. I also think having only one try/catch/finally clause per
method is also easier to understand.
My more thought out $0.02,
Mark Wehby
-----Original Message-----
From: esumerfd@xxxxxxxxx [mailto:esumerfd@xxxxxxxxx] On Behalf Of Edward
Sumerfield
Sent: Wednesday, October 18, 2006 11:05 AM
To: users@xxxxxxxxxx
Subject: Re: [cinjug-users] Brandan Jones Presentation
On 10/18/06, Wehby, Mark <mwehby@xxxxxxxxxx> wrote:
> I generally agree with the comments below, except for the part on
> Exceptions. I prefer unchecked exceptions over checked,
Agreed, but this is where we part ways.
> but I usually
> do not find it useful to define a hierarchy of unchecked exceptions.
> Most of the time, the calling code can figure out what to do by using
> the getCause() method on the Throwable class.
I would call this a leaky abstraction code smell.
http://en.wikipedia.org/wiki/Leaky_abstraction
http://www.joelonsoftware.com/articles/LeakyAbstractions.html
The cause of the exception is useful for logging and debugging but if
your code is making decisions based on it then you are assuming an
implementation of the API you are using.
The API of a class exposes methods, and thrown exceptions (and public
properties but we good little programmers always use getters). This
API needs to be strictly enforced by the caller or changes in
implementation behind the API are likely to break the callers code.
For this reason, enhancing the API to tell the caller enough
information so that they can determine what happened without exposing
any implementation secrets is a more decoupled, less leaky, approach.
> Unless I can add "helper"
> methods on the derived unchecked runtime exception class, I try to use
a
> RuntimeException from the JDK.
Use of any of the root exceptions if a bad idea. It's about "positive
programming". I believe there is a more common term for this approach
but I can put my finger on it. Anyway, each line of code you write
should serve one purpose and that purpose should not accidentally
achieve side effects.
Catching Exception or RuntimeException means that you will be catching
any number of other situations that you do not intend to catch. Say
someone adds a call to Integer.parseInt lower in the stack somewhere
you will now catch that as well not knowing that it is a different
situation that you are catching. This can hide bugs and cause
inaccurate/unintended reactions to situations. Its the unintended part
that is dangerous and not "positive programming".
> I am not sure if this is a great example of a useful "helper" method,
> but say you have a web site where users must subscribe in order to
read
> articles. The first time a user visits, he/she fills in the username
> and password fields and submits it for approval. On the server, there
> is code that checks to make sure the username is unique. In the case
> where the username is not unique, the code throws a RuntimeException
> with a useful message. On the other hand, we could try to help by
> creating a custom DuplicateNameException that extends
RuntimeException.
> We could add a method called "suggestedUsernames". This method could
> use some fancy algorithm to generate unique names that are similar to
> the original username.
The DuplicateNameException is good because it becomes part of your
public API, these "helper" methods are a way to allow the caller to
access information like the caused-by without becoming coupled to the
type. So you might have a method isDatabaseConnected or something that
checks for a caused by of a SQLException but doesn't expose the
SQLException type to the caller.
Now the suggestedUsernames is interesting because it exists in a
domain type that has nothing to do with usernames or any kind of fancy
algorithm class. I would argue that the exception class should serve
as a simple notification class, only supplying additional information
about the situation. Then another domain class would be used to
determine suggested names. FancyUserNameSuggestor or something.
In the past I have used specific RuntimeExceptions as stack
notifications. Imagine a web server that receives requests through a
common handler. Processing continues down the stack until it finds an
error that the common handler needs to react to. You could either
choose to return some common Result instance back up the stack or
throw an exception that you know the common handler is going to catch
and handle.
This becomes a sort of notification mechanism allowing deep methods to
talk to shallow methods. We only used it in two situations and it was
very useful. We made the rule that only web layer code could perform
the throws, no business or DAO layer should couple themselves into a
"stack" based communication mechanism. After all, they might be moved
to an ejb container somewhere one day and we wouldn't want any stack
coupling.
As a side note, the term "helper" should be eradicated from our
dictionary. All features need to be implemented in a domain
appropriate class to optimize reusability and flexibility. Helper
classes are like constant classes, they serve as the procedural
programmers dumping ground for stuff that they can't work where to put
it. You used the term differently in that you intend methods to go in
those classes. It is just a distasteful term.
> Just my $0.02
> Mark Wehby
>
> -----Original Message-----
> From: esumerfd@xxxxxxxxx [mailto:esumerfd@xxxxxxxxx] On Behalf Of
Edward
> Sumerfield
> Sent: Tuesday, October 17, 2006 4:39 PM
> To: users@xxxxxxxxxx
> Subject: [cinjug-users] Brandan Jones Presentation
>
> A great presentation by Brandon last night. As always, I learned
> something new. CinJug rocks.
>
> It sounded like there were some java newbies there so I thought it
> would be interesting to pick apart the parts of Brandon's code that
> were bad. I hope all the newbies are on the list and that no one takes
> "newbie" in any derogatory sense.
>
> We all understand that Brandon's presentation was focused on Patterns
> so this is no reflection on his code since it served its purpose well.
> Instead it may serve as a fun extension to that code to look at what
> is good and bad about what we do each day.
>
> If you don't recognize my name and were at the meeting you will know
> me by "I am the runner, execute".
>
> Exceptions
> ----------------
> The exceptions were used in the file io process and it brings up lots
> of good practice issues.
>
> try {
> Scanner scanner = new Scanner(file);
> ...
> }
> catch (FileNotFoundException e)
> {
> throw new RuntimeException(e);
> }
>
> 1) Should we every throw a RuntimeException?
>
> Your applications should never throw or catch Exception or
> RuntimeException. Instead, your application should use a well defined
> exception hierarchy that allows exceptions to be addressed at
> appropriate levels.
>
> In this case we should throw something like
> FileNotFoundRuntimeException. There might also be an
> InvalidFileFormatRuntimeException that can be thrown if the scanner
> doesn't find what it expects. This is text based UML, where ^ means
> extends.
>
> RuntimeException
> ^
> FileImportFailedRuntimeException
> ^
> ^
> InvalidFileFormatRuntimeException FileNotFoundRuntimeException
>
> With this structure the thrower can now throw an exception that is
> specific to the problem. The catcher can catch the specific or the
> higher level exception depending on how they want to manage the
> problem.
>
> 2) Should we use checked or unchecked exceptions?
>
> There is a great deal of debate in the industry about which are the
> better approach. You will tend to find that people that like strongly
> typed languages also like checked exceptions, where as those who
> prefer dynamically typed languages tend toward the unchecked variety.
>
> I will give you my perspective and let others posture the
alternatives.
>
> Checked exceptions force the user of an API to deal with the problems
> that the API might cause. At first sounding this seems good.
> Unfortunately, in reality the "force" portion of this process is
> thrust upon the developer before they really know what the program
> should do about it. The result is that code gets a lot of empty catch
> blocks or System.out.println's.
>
> The hardest bugs to find are those that don't leave any evidence and
> empty catch blocks are the worst. Even works that null pointer
> exceptions but we will get to those next.
>
> I am a proponent of forcing real errors to the surface as fast as
> possible which means crashing the program in front of the developer.
> To do this I propose only using RuntimeExceptions and allowing the
> developer to choose the best time and place to insert the catch
> statement.
>
> When the developer has got to the point of running the program and
> finding the error situation they are at the right point to know what
> to do about it and can insert the catch of the runtime exception and
> the appropriate behaviour.
>
> The other advantage of the RuntimeException is that you don't have to
> declare it on all the methods that might end up calling a thrower.
> This loosens the coupling between your APIs and reduces the number of
> files that change when you change you mind about the exceptions to
> throw.
>
> Nulls
> -------
> One of the early versions of Brandon's factory method went something
> like this:
>
> if (param.equals("something")) {
> return new Something();
> }
> else if (param.equals("anotherthis")) {
> return new AnotherThing();
> }
>
> return null;
>
> Nulls are the death of a system. Traditionally developed software
> follows a few predictable phases, code, test, fix bugs, release to
> production, fix all the null pointer exceptions.
>
> These bugs are hard to find because the exception report doesn't tell
> you where the error occurred, only where the null value was eventually
> used.
>
> The most common fix is "if (x != null)". This is not the right answer
> but sometimes we are forced into bad code by bad 3rd party libraries.
> What can I say, most of them suck. Yes, I am opinionated, and they
> still suck.
>
> The correct answer is to not produce nulls. If no code ever returns a
> null then you will never get a NullPointerException.
>
> Brandon's final version converted over to throwing an exception (not
> the correct type but we have talked of that already) so the factory
> can not produce a null.
>
> Take this to the next level. A method getList(). What should it do if
> there is no list available. It should return an empty array list. Sun
> was kind enough to supply is with a simple version in
> Collections.EMPTY_LIST or emptyList in 1.5.
>
> In all cases there are ways to design your APIs to prevent nulls from
> being produced. Take the extra time to do this because you will earn
> it back in droves as you sit back and watch your not so well educated
> colleagues shouting about a bug that "just can't be happening".
>
> Single Return Statements
> -------------------------------------
> This one is slightly contentious as well. A method should only have
> one return statement in it. Brandon's original factory method had 4.
> This becomes a real problem in longer methods when you can't always
> tell if the code nearer the bottom will get executed.
>
> Ideally all you methods should be short as well but these features
> play against each other as a nice piece of code gets hacked on by 20
> different developers over a few years.
>
> Some argue that in short methods it doesn't matter. I argue that short
> methods become long methods so it always matters. I never use more
> than one return statement even on the simplest cases.
>
> MonthlyCommand command;
>
> if (param.equals("something")) {
> command = new Something();
> }
> else if (param.equals("anotherthis")) {
> command = new AnotherThing();
> }
> else {
> throw new InvalidFileTypeRuntimeException("Ooops");
> }
>
> return command;
>
> Brandon's final reflective approach got rid of this problem anyway and
> you will find that good design principles will tend to get rid of
> these bad coding habits auto magically. It's all about the the strong
> karma that you develop with each excellent piece of code.
>
> DRY
> ------
> Don't Repeat Yourself. We all know that we shouldn't duplicate code.
> Copy Paste is a bugs best friend, the easiest way to spread through a
> system. Brandon's code ended up with a set of MonthlyCommand
> implementations that included a try catch for FileNotFoundException, a
> scanner and a set of file specific fields.
>
> public void execute(file) {
> try {
> Scanner scanner = new Scanner(file);
> String name = scanner.next();
> String amount = scanner.nextInt();
>
> System.out.println(name + " " + amount);
> }
> catch (FileNotFoundException e)
> {
> throw new NoInputFileRuntimeException("Could not find
> input file", e);
> }
> }
>
> So how DRY is this code. There are four lines of duplicate code here.
> Lets try a simple pull up refactoring to get rid of them:
>
> First in the abstract base class.
>
> public void execute(file) {
> try {
> Scanner scanner = new Scanner(file);
> parseFields(scanner);
> }
> catch (FileNotFoundException e)
> {
> throw new NoInputFileRuntimeException("Could not find
> "+file.getName(), e);
> }
> }
>
> protected abstract void parseFields();
>
> Then in the original MonthlyCommand implementation that is not
> extending our abstract base class.
>
> public void parseFields(Scanner scanner)
> {
> String name = scanner.next();
> String amount = scanner.nextInt();
>
> System.out.println(name + " " + amount);
> }
>
> It's a very simple change, it centralizes the error handling which is
> really important and cuts down the amount you have to type in for each
> command you write. You know there is a limit to the number of keys you
> can press in a lifetime, use them wisely.
>
> Open Close Principle
> -------------------------------
> Brandon talked about using these patterns to reduce the number of
> classes that you have to edit to add new functionality. This is called
> the Open Close Principle. Once you live by this you will significantly
> improve the quality of the API's that you design.
>
> Code should be open for extension
> and closed for modification.
>
> http://en.wikipedia.org/wiki/Open/closed_principle
>
> Unit Tests
> --------------
> Brandon mentioned his commitment to unit testing at the end of the
> presentation. I hope everyone took this to heart. Test first
> development (also called Test Driven Development) is the most
> significant advancement in software engineering practice since OO.
> Every class/method you write must have a corresponding unit test.
>
> http://en.wikipedia.org/wiki/Test_driven_development
>
> I know there are a few Universities starting to teach TDD but it is by
> no means mainstream yet. Most professionals already working today do
> not have an understanding of TDD. We still live in a world of "let the
> QA group do the testing for me".
>
> If I interview someone that doesn't know or use TDD, I will not hire
> them. It's that simple.
>
> Perhaps Brandon can comment on how he addresses this subject.
>
> Thanks again for the presentation Brandon.
>
> --
> Ed
>
> ---------
> You may unsubscribe from this mailing list
> by sending a blank email addressed to:
> users-unsubscribe@xxxxxxxxxx
>
> --
> Find additional help by sending a blank email
> addressed to:
> users-help@xxxxxxxxxx
>
>
> ---------
> You may unsubscribe from this mailing list
> by sending a blank email addressed to:
> users-unsubscribe@xxxxxxxxxx
>
> --
> Find additional help by sending a blank email
> addressed to:
> users-help@xxxxxxxxxx
>
>
--
Ed
---------
You may unsubscribe from this mailing list
by sending a blank email addressed to:
users-unsubscribe@xxxxxxxxxx
--
Find additional help by sending a blank email
addressed to:
users-help@xxxxxxxxxx
|