The D
says "depend on abstractions, not concrete types" - that doesn't mean to box your value types into object
!
In fact, this:
private int _pin;
Contradicts this:
if (pin.ToString().Length != 4)
{
throw new ArgumentException("Pin must be exactly 4 digits long integer", "pin");
}
If your pin
is meant to work like a string, then it should be stored as a string
. If my pin is 0072
, I have a 4-digit pin, but you're storing it as 72
and throwing an ArgumentException
at me.
Exposing object
in your public API is horrible practice - nobody knows what you're expecting to receive! Worse, there's no XML documentation on your public members, so the only way to find out, is to dig into your code.
Come to think about it, with respect to the I
of SOLID, I think your interface should look like this:
public interface IPinAuthorization
{
bool Validate(SecureString pin);
}
And a PIN shouldn't be that easy to modify. Consider making the type immutable, and asking for the current
PIN before accepting a new
one:
public interface IModifiablePin
{
IPinAuthorization Modify(SecureString current, SecureString @new);
}
You could have a more secure PinAuthorization
implementation like this:
public class PinAuthorization : IPinAuthorization, IModifiablePin, IDisposable
{
private readonly SecureString _pin;
public PinAuthorization(SecureString pin)
{
ValidateInternal(pin); // see comments below
_pin = pin;
}
private void ValidateInternal(SecureString pin)
{
if (pin.Length != 4)
{
throw new ArgumentException("PIN must be 4 digits.", pin);
}
// further validation logic would break the security of the string.
// this is an indication that it's not this type's responsibility
// to know how to validate itself.
}
public bool Validate(SecureString pin)
{
return pin == _pin;
}
public IPinAuthorization Change(SecureString current, SecureString @new)
{
if (_pin != current)
{
throw new ArgumentException("Invalid PIN.", current);
}
return new PinAuthorization(@new);
}
public void Dispose()
{
_pin.Dispose();
}
}
Now, if you want to implement some FingerprintAuthorization
, you can make it implement IPinAuthorization
and validate against a SecureString
-encoded finger print (however you want to do that), and the type will not be implementing IModifiablePin
, since you don't change your finger prints. Or do you? Well, at least you have the flexibility of doing either. At a glance though, it seems the class has so little responsibility, that it possibly wouldn't even need to be reimplemented or adapted in any way.