Non c'è modo di richiedere un'implementazione per generare un'eccezione attraverso un'interfaccia, anche in linguaggi come Java, dove puoi dichiarare che un metodo può generare un'eccezione.
Potrebbe esserci un modo per garantire (in una certa misura ma non assolutamente) che venga lanciata un'eccezione. Puoi creare un'implementazione astratta della tua interfaccia. È quindi possibile implementare il metodo GetUser
come finale nella classe astratta e utilizzare il modello di strategia per chiamare un altro membro protetto della sottoclasse e generare un'eccezione se restituisce qualcosa di diverso da un utente valido (come null). Questo può ancora cadere se, per esempio, l'altro sviluppatore restituisce un tipo di oggetto nullo User
, ma dovrebbero davvero lavorare per sovvertire l'intento qui. Potrebbero anche solo reimplementare la tua interfaccia, anche questa male, quindi potresti prendere in considerazione la possibilità di sostituire completamente l'interfaccia con la classe astratta.
(Risultati simili possono essere ottenuti utilizzando la delega anziché la sottoclasse con qualcosa come un decoratore di wrapping.)
Un'altra opzione potrebbe essere quella di creare una suite di test di conformità che tutti i codici di implementazione debbano passare per essere inclusi. Quanto è efficace dipende da quanto controllo hai sul collegamento dell'altro codice nel tuo.
Sono anche d'accordo con gli altri sul fatto che una chiara documentazione e comunicazione sono dati quando è richiesto un requisito come questo ma non può essere completamente applicato nel codice.
Esempi di codice:
Metodo sottoclasse:
public abstract class ExceptionalUserRepository : IUserRepository
{
public sealed User GetUser(int user_id)
{
User u = FindUserByID(user_id);
if(u == null)
{
throw new UserNotFoundException();
}
return u;
}
// subclasses implement this method instead
protected abstract User FindUserByID(int user_id);
// More code here
}
Metodo decoratore:
public sealed class DecoratedUserRepository : IUserRepository
{
private readonly IUserRepository _userRepository;
public DecoratedUserRepository(IUserRepository userRepository)
{
_userRepository = userRepository;
}
public User GetUser(int user_id)
{
User u = _userRepository.GetUser(user_id);
if(u == null)
{
throw new UserNotFoundException();
}
return u;
}
// More code here
}
public class SomeClass
{
private readonly IUserRepository _userRepository;
// They now *have* to pass in exactly what you want
public SomeClass(DecoratedUserRepository userRepository)
{
_userRepository = userRepository;
}
// More code
}
Un ultimo punto rapido che voglio fare che ho dimenticato prima è che, facendo uno di questi, ti stai legando a un'implementazione più specifica, il che significa che gli sviluppatori di implementazione ottengono molta meno libertà.