Java and single inheritance

Started by
251 comments, last by zacharya 20 years, 1 month ago
quote:Original post by Mayrel
But the caller of the caller might .
void doFooWithDB (Database db){  while (true) {    try {      Database db = new Database;      FooConsumer fc = new FooConsumer(new FooProvider());      fc.consume();      break;    } catch (DBConnectionDropped e) {      db.reconnect();    }  }}  

We cannot check that the DBConnectionDropped exception is being handled.

Using a FooProvidingException as defined in my previous post, the catch block would become :
catch(FooProvidingException fpe){   //if we can't make assumption on FooProvider implementation   //we handle only the fact we can't get a Foo, not the reason   //for example :   fpe.printStackTrace();   System.exit(-1);   //else we know what implementation is used (here FooProviderDB)   //we get the encapsulated exception and handle it properly   Exception e = fpe.getException();   if(e instanceof DBConnectionDropped)   {       ...   }} 

quote:Original post by Mayrel
quote:
The caller should react to the fact that he can't get its Foo object and not care about possible resource leak (for example).

You have to be joking? I should write a program that doesn't care about resource leaks?

No, I wasn't clear enough. Potential resource leaks should be handled by the FooProvider implementation, since it knows how stuff is made and how it should be cleaned.

A generic method doFoo(FooProvider fp) is not equipped to deal with a SQLException, a FileNotFoundException or a premature EOF.
However it knows that without Foo it can't work properly. So doFoo should contain the logic to handle the fact it can't get its Foo object, while resource leaks should be handled directly by the FooProvider impl.

With that in mind, I would modify the code from your FooProvider as follow :
public Foo get() throws FooProviderException{    Foo f;    try    {        Foo f = new Foo(rs.getField("Name"), rs.getField("Height"));        rs.moveNext();    }    catch(DBException e)    {        //clean resources and state        ...        throw new FooProviderException(msg,e);    }    return f;};  



[edited by - overnhet on February 29, 2004 3:37:23 PM]
Advertisement
quote:Original post by overnhet
We cannot check that the DBConnectionDropped exception is being handled.

Using a FooProvidingException as defined in my previous post, the catch block would become :
catch(FooProvidingException fpe){   //if we can''t make assumption on FooProvider implementation   //we handle only the fact we can''t get a Foo, not the reason   //for example :   fpe.printStackTrace();   System.exit(-1);   //else we know what implementation is used (here FooProviderDB)   //we get the encapsulated exception and handle it properly   Exception e = fpe.getException();   if(e instanceof DBConnectionDropped)   {       ...   }} 


Yes, but this misses an important point. With the above method, errors are not detected until they occur. With checked exceptions, potential errors are detected before they occur. This means that the single benefit of checked exceptions -- detection of potential errors -- is nullified in this case.

A more sensible approach to the above example, incidentally, would be to rethrow the exception if it couldn''t be handled. As it stands, you couldn''t nest FooProviderExceptions, which is no good.

Also, it relies upon the programmer doing extra manual error checking, which is hardly ideal.
quote:
quote:Original post by Mayrel
quote:
The caller should react to the fact that he can''t get its Foo object and not care about possible resource leak (for example).

You have to be joking? I should write a program that doesn''t care about resource leaks?

No, I wasn''t clear enough. Potential resource leaks should be handled by the FooProvider implementation, since it knows how stuff is made and how it should be cleaned.

Well, that''s fine. But that should be true even if exceptions aren''t being used. FooConsumer should not be responsible for freeing the resources used by implementors of the abstract FooProvider interface, that''s just good design policy.
quote:
A generic method doFoo(FooProvider fp) is not equipped to deal with a SQLException, a FileNotFoundException or a premature EOF.

No. But my doFooWithDB method isn''t generic. It can deal with an SQLException, FieldNotFoundException or any number of database-related exceptions.
quote:
However it knows that without Foo it can''t work properly. So doFoo should contain the logic to handle the fact it can''t get its Foo object, while resource leaks should be handled directly by the FooProvider impl.

With that in mind, I would modify the code from your FooProvider as follow :
public Foo get() throws FooProviderException{    Foo f;    try    {        Foo f = new Foo(rs.getField("Name"), rs.getField("Height"));        rs.moveNext();    }    catch(DBException e)    {        //clean resources and state        ...        throw new FooProviderException(msg,e);    }    return f;}; 


The issue with this modification being that FooProviderDB doesn''t necessarily have sufficient knowledge to sort things out. It may be that we''re meant to try another table, another database, another FooProvider altogether, or just give up. To communicate that information to the level of FooProviderDB.get requires either that we store it in static variables, which just isn''t nice, or we send it down the call chain, which is also not nice.
CoV
quote:Original post by Mayrel
Yes, but this misses an important point. With the above method, errors are not detected until they occur. With checked exceptions, potential errors are detected before they occur. This means that the single benefit of checked exceptions -- detection of potential errors -- is nullified in this case.

Sorry but I don''t get your point here. Exceptions are caught and handled if necessary, that''s the whole point of the method. Nesting allows a "multiresolution" handling of exceptions :
* if you know what impl is used and what kind of exceptions it could throw, you handle them (low level)
* else you are using interfaces for abstraction and you don''t have to handle the nested exception because you don''t know what step in the process could have thrown it (high level)

quote:Original post by Mayrel
A more sensible approach to the above example, incidentally, would be to rethrow the exception if it couldn''t be handled.

I usually don''t rethrow the exception because :
* it impacts performance
* a simple bunch of if(x instanceof X) was sufficient in all the cases I had to face so far

quote:Original post by Mayrel
As it stands, you couldn''t nest FooProviderExceptions, which is no good.

FooProviderException makes no assumption on the type of exception that is nested, nor the nesting depth. Therefore you could nest several exceptions. But it would add a great deal of complexity which is usually unwanted and unuseful. KISS

quote:Original post by Mayrel
Also, it relies upon the programmer doing extra manual error checking, which is hardly ideal.

No, because the programmer is not required to handle the nested exception : he''ll do it only if he knows more details on the specific FooProvider impl he is using. It will obviously makes him type a few more LOC, but laziness and abstraction aren''t always compatible. One doesn''t use interfaces to type less LOC, but to have less impacts when changes must be made.

quote:Original post by Mayrel
No. But my doFooWithDB method isn''t generic. It can deal with an SQLException, FieldNotFoundException or any number of database-related exceptions.

In doFooWithDB you use a particular impl of the interface therefore you know what kind of exception could be nested and you can handle them at low level. As I said before, nesting exception is used to give you control without losing abstraction.

quote:Original post by Mayrel
The issue with this modification being that FooProviderDB doesn''t necessarily have sufficient knowledge to sort things out. It may be that we''re meant to try another table, another database, another FooProvider altogether, or just give up.

Again, I don''t get your point. Should doFooWithDB have to try another FooProvider, it wouldn''t be done in FooProviderDB but rather in the catch(FooProviderException) block. It seems to be a high level case rather than a low level one. The same goes for database and table since you FooProviderDB is tied to a specific database.

Well unless you bring up some really interesting problem, I''d rather let this thread die. With Java exception handling we are already way off topic and this is not the kind of problem I feel passionated by, especially since it looks solved to me. And I have yet to code a lot of nasty stuff for my project

This topic is closed to new replies.

Advertisement