The question title is probably too abstract, so let me provide a particular example of what I have in mind:
There is a webservice that encapsulates a process of changing passwords for users of a distributed system. The webservice accepts user's login, his old password and a new password. Based on this input, it can return one of the following three results:
- In case user was not found, or his old password does not match, it will simply return with HTTP 403 Forbidden.
- Otherwise, it takes a new password and makes sure that it conforms to a password policy (e.g. it is long enough, contains a proper mix of letters and numbers, etc.). If it does not, it will return an XML describing why the password does not conform to the policy.
- Otherwise, it will change the password and return an XML containing an expiration date of the new password.
Now, I'd like to design a class, ideally with a single method, to encapsulate working with this webservice. My first shot was this:
public class PasswordManagementWebService
{
public ChangePasswordResult ChangePassword(string login, string oldPassword, string newPassword)
{
ChangePasswordResult result;
// send input to websevice, it's not important how; the httpResponse
// will contain a response from webservice
var httpResponse;
if (HasAuthenticationFailed(httpResponse)
{
throw new AuthenticationException();
}
else if (WasPasswordSuccessfullyChanged(httpResponse))
{
result = new ChangePasswordSuccessfulResult(httpResponse);
}
else
{
result = new ChangePasswordUnsuccessfulResult(httpResponse);
}
return result;
}
}
public abstract class ChangePasswordResult
{
public abstract bool WasSuccessful { get; }
}
public abstract class ChangePasswordSuccessfulResult
{
public ChangePasswordSuccessfulResult(HttpResponse httpResponse)
{
// initialize the class from the httpResponse
}
public override bool WasSuccessful { get { return true; } }
public DateTime ExpirationDate { get; private set; }
}
public abstract class ChangePasswordUnsuccessfulResult
{
public ChangePasswordUnsuccessfulResult(HttpResponse httpResponse)
{
// initialize the class from the httpResponse
}
public override bool WasSuccessful { get { return false; } }
public bool WasPasswordLongEnough { get; private set; }
public bool DoesPasswordHaveToContainNumbers { get; private set; }
// ... etc.
}
As you can see, I've decided to use separate classes for return cases #2 and #3 - I could have used a single class with a boolean, but it feels like a smell, the class would have no clear purpose. With two separate classes, an user of my PasswordManagementWebService
class now has to know which classes inherit from ChangePasswordResult
and to cast to a correct one based on the WasSuccessful
property. While I now do have a nice, laser-focused classes, I made a life of my users more difficult than it should be.
As for the case #1, I've just decided to throw an exception. I could have created a separate exception for the case #2, too, and only return something from the method when the password was successfully changed. However, this doesn't feel right - I don't think that a new password being invalid is a state exceptional enough to warrant throwing an exception.
I am not very sure how would I design things were there more than two un-exceptional result types from the webservice. Probably, I would change a type of WasSuccessful
property from boolean to an enum and rename it to ResultType
, adding a dedicated class inherited from ChangePasswordResult
for each possible ResultType
.
Finally, to the actual question: Is this design approach (i.e. having one abstract class and forcing clients to cast to a correct result based on a property) a correct one when dealing with problems like this? If yes, is there a way to improve it (perhaps with a different strategy for when to throw exceptions vs. return results)? If no, what would you recommend?