Sto progettando una classe che ha uno stato. Mi chiedo se dovrei esporre questo stato nell'interfaccia in modo da consentire ad un decoratore di arricchire la logica di transizione dello stato.
Il mio disegno espone l'accesso allo stato?
Facciamo un esempio e supponiamo di avere questa interfaccia:
public interface ILoginService
{
void Login();
void Logout();
bool IsLoggedIn { get; } // <-- should that be exposed?
}
Un'implementazione potrebbe essere simile a questa:
public class LoginService : ILoginService
{
private bool _loggedIn;
public IsLoggedIn => _loggedIn;
public void Login()
{
if (!IsLoggedIn) // <-- should that be done?
{ ... } // Login procedure
}
public void Logout() { ... }
}
Quale sarebbe l'approccio giusto?
-
Variante A
Esporre la proprietà
IsLoggedIn
tramite l'interfaccia, ma non controllareIsLoggedIn
prima della procedura di accesso all'interno dell'implementazione. Motivo: se espongo la proprietà, mi aspetto che il client chiamante la gestisca correttamente. Un decoratore o un cliente può forzare la procedura di accesso ignorando la proprietàIsLoggedIn
(a causa della conoscenza avanzata / migliore). Svantaggio: posso aspettarmi dagli implementatori che non debbano controllare la proprietàIsLoggedIn
internamente? -
Variante B
Esporre la proprietà
IsLoggedIn
tramite l'interfaccia e controllareIsLoggedIn
prima della procedura di accesso all'interno dell'implementazione. Motivo: posso solo tornare nella funzione e quindi evitare le eccezioni con "double login". Svantaggio: un cliente o un decoratore (con conoscenze avanzate) non può forzare l'implementazione a effettuare nuovamente il login, perché controlla sempre lo stato stesso -
Variante C
Non esporre la proprietà
IsLoggedIn
tramite l'interfaccia, ma controllaIsLoggedIn
prima della procedura di accesso all'interno dell'implementazione. Motivo: il cliente non deve preoccuparsi dello stato e può fidarsi dell'implementazione di non "doppio login". Svantaggio: un decoratore non può decorare la proprietà IsLoggedIn o forzare l'implementazione a riconnettersi (perché il decoratore potrebbe avere una certa conoscenza che il login è stato rifiutato / cancellato qualche tempo dopo in background)
Problema pratico alla base di questa domanda: come arricchire i cambiamenti di stato?
In alcuni dei nostri sistemi, il login viene scartato se viene eseguito un reset dell'hardware. Volevo gestire questo tramite un decoratore che è in grado di rilevare questi ripristini al fine di rispettare l'SRP.
Ma per farlo funzionare, il decoratore deve essere in grado di forzare l'accesso, anche se lo stato interno dell'implementazione dice che è già stato effettuato il login. Quindi andrei per la variante A, ma non sono sicuro se posso aspettarmi di non controllare lo stato internamente dagli sviluppatori.
Aggiornamento - La mia idea di un decoratore
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() { ... }
}
Le mie aspettative sono valide in questo scenario?