I am designing a class that has a state. I wonder if I should expose that state in the interface in view of allowing a decorator to enrich the state transition logic.
Shall my design expose access to the state?
Let's take an example and suppose that I have this interface:
public interface ILoginService
{
void Login();
void Logout();
bool IsLoggedIn { get; } // <-- should that be exposed?
}
One implementation could look like this:
public class LoginService : ILoginService
{
private bool _loggedIn;
public IsLoggedIn => _loggedIn;
public void Login()
{
if (!IsLoggedIn) // <-- should that be done?
{ ... } // Login procedure
}
public void Logout() { ... }
}
What would be the right approach?
Variant A
Expose the
IsLoggedIn
property via interface but don't checkIsLoggedIn
before login procedure within implementation. Reason: If I expose the property, I expect the calling client to handle it correctly. A decorator or client can force the login procedure disregarding theIsLoggedIn
property (because of advanced/better knowledge). Disadvantage: Can I expect from implemeters that they must not check theIsLoggedIn
property internally?Variant B
Expose the
IsLoggedIn
property via interface and checkIsLoggedIn
before login procedure within implementation. Reason: I can just return in the function and so avoid exceptions by "double login". Disadvantage: A client or decorator (with advanced knowledge) cannot force the implementation to login again, because it always checks the the state itselfVariant C
Don't expose the
IsLoggedIn
property via interface but checkIsLoggedIn
before login procedure within implementation. Reason: The client need not care about the state and can trust on the implementation to not "double login". Disadvantage: A decorator cannot decorate the IsLoggedIn property or force the implementation to login again (because the decorator might have some knowledge that the login was rejected / cancelled some time later in background)
Practical problem behind that question: how to enrich the state changes?
In some of our systems the login is discarded if a hardware reset is done. I wanted to handle this via a decorator that is able to detect these resets in order to comply with the SRP.
But for this to work the decorator must be able to force the login, even if the internal state of the implementation says it is already logged in. So I would go for variant A but I am not sure if I can expect to not check the state internally from developers.
Update - My idea of a decorator
public class ResetAwareDecorator : ILoginService
{
private readonly ILoginService _decoratee;
private readonly IResetDetector _reset;
private bool _newResetAfterLastLogin;
public ResetAwareDecorator(ILoginService decoratee, IResetDetector reset)
{ ... }
public bool IsLoggedIn => _decoratee.IsLoggedIn && !_newResetAfterLastLogin; // I would expect the client to test this before calling Login();
public void Login()
{
_decoratee.Login(); // I would expect the decoratee to execute Login() independent of the state _decoratee.IsLoggedIn
_newResetAfterLastLogin = false;
}
public void Logout() { ... }
}
Are my expectations valid in this scenario?