For example a FileLoader class with a LoadFile(fileName) method - if a
file asked to load does not exist then normal execution cannot
continue.
Also, a way to avoid an exception should, where possible, be provided.
In the above example the answer would be to add a
DoesFileExist(fileName) method to the FileLoader class. This allows
the user of the object to do:
if(FileLoader.DoesFileExist(fileName)
{
File file = FileLoader.LoadFile(fileName);
}
else
{
ShowMessage("File {0} could not be found", fileName);
}
So, how should a Login to an application be handled? If the wrong
details are provided should an exception be thrown or should an
LoginStatus object be returned providing details to the failure? Also,
DoesUserExist() does not nessasarily apply to a Login scenario -
especially seeing that logging in is a black or white situation. And,
does a failure to login prevent normal flow or is a login failure just
a branching of a normal flow?
Grateful for input.
public enum LoginStatus
{
Authenticated,
BadUsername,
BadPassword,
UserNotFound,
AccountLocked
}
The authentication method that would return the result would be apart
of the business/application logic layer. Then in the presentation layer
you could decide what degree of info you would disclose to the user.
i.e. not much info = "bad username or password" or more info = "your
username doesn't exist"
As far as exceptions go in general, I would agree with your statement:
"only use exceptions when something exceptional happens - i.e. the
normal flow of a program (or component) cannot continue."
There is a good article on exceptions at:
However, Exceptions can be very helpful when trying to write software
that works correctly. When done right, using exceptions liberally can
make an application more robust.
Microsoft has taken this path. Whenever something happens which is not
in line with the expected program flow, an Exception is thrown.
Although hardly a critical error, Int32.Parse("not an integer") throws
an Exception.
Partially it's a matter of taste. I use Exceptions a lot because I
prefer erring on the side of correctness to erring on the side of
performance. Also, I tend to perceive code using lots of exceptions as
being more readable.
For instance, for a login system, quite probably you'd create a User
class containing user information:
class User {
public string Username {}
public string PasswordHash {}
}
If this class would store all users, it would have to provide means to
access the invidual user objects:
public static User ByUserName(string username);
Basically, there would be two methods to handle an unknown username:
1) Throwing an exception
2) Returning null
I believe throwing an Exception is the better strategy. Strategy #2
requires ALL the code to check for null. If this check is omitted,
there is the risk of a NullPointerException, which does little to
explain what went wrong.
Therefore if I had to write login code it'd look kinda like this:
public Session Login(string username, string password) {
User user = User.ByUserName(username); // throws an exception if
user not found
if(user.HasPassword(password)) {
return new Session(user);
} else {
throw new InvalidPasswordException(username);
}
}
Hope it helps!
Cheers,
Bram
No matter what you do- error codes, enums, bools, you still have to
program against the error logic. Exceptions provide a more structured
approach at the cost of performance. Error codes can change
arbitrarily, enums are safer but handling error paths could slip
through the cracks. In general, users are probably not going to be
failing logins a lot, are they? I guess in the end what do you think
will be better:
try {
User.Login(username, password);
}
Catch(NoUserException ex)
{
ShowRegisterUser();
}
Catch(InvalidPasswordExcetion ex)
{
ShowBadPasswordMessage();
}
Catch(Exception ex)
{
ShowGenericError();
}
Finally
{
ShowHomePage();
}
Or
myLoginStatusEnum = User.Login(Username, Password);
switch(myLoginStatusEnum)
{
case Success:
ShowHomePage(); break;
case InvalidUser: SHowRegister(); break;
Default: Uh-oh, the enum values have changes, what do i do! Or do i
login? Is the user null? What to do then?
}
I guess exceptions are the safest bet in terms of structured control
flow under various scenarios.
With your case statement, it should be used that unless the user is
explicitly authenticated (i.e. User.Login() returns LoginStatus.Sucess)
that the login attempt should fail. From there it is up to the
developer to how they handle the failure info given (BadUsername,
BadPassword, UserNotFound, AccountLocked ...Other) that means that the
default statement in a switch is an unknown/generic failure.