Es gibt keine Möglichkeit, eine Implementierung zum Auslösen einer Ausnahme über eine Schnittstelle zu verlangen , selbst in Sprachen wie Java, in denen Sie deklarieren können, dass eine Methode eine Ausnahme auslösen könnte.
Es kann eine Möglichkeit geben, sicherzustellen (bis zu einem gewissen Grad, aber nicht absolut), dass eine Ausnahme ausgelöst wird. Sie können eine abstrakte Implementierung Ihrer Schnittstelle erstellen. Sie können die GetUser
Methode dann als final in der abstrakten Klasse implementieren und mithilfe des Strategiemusters ein anderes, geschütztes Mitglied der Unterklasse aufrufen und eine Ausnahme auslösen, wenn etwas anderes als ein gültiger Benutzer zurückgegeben wird (z. B. null). Dies kann immer noch zum Erliegen kommen, wenn der andere Entwickler beispielsweise einen Nullobjekttyp zurückgibt User
, aber er müsste wirklich arbeiten, um die Absicht hier zu untergraben. Sie könnten auch nur Ihre Schnittstelle neu implementieren, auch schlecht, so dass Sie in Betracht ziehen könnten, die Schnittstelle vollständig durch die abstrakte Klasse zu ersetzen.
(Ähnliche Ergebnisse können mit der Delegierung erzielt werden, anstatt mit einem Verpackungsdekorateur eine Unterklasse zu erstellen.)
Eine andere Option könnte darin bestehen, eine Konformitätstestsuite zu erstellen, die der gesamte implementierende Code übergeben muss, um aufgenommen zu werden. Wie effektiv dies ist, hängt davon ab, wie viel Kontrolle Sie über die Verknüpfung des anderen Codes mit Ihrem Code haben.
Ich stimme auch anderen zu, dass eine klare Dokumentation und Kommunikation selbstverständlich sind, wenn eine solche Anforderung erwartet wird, aber im Code nicht vollständig durchgesetzt werden kann.
Codebeispiele:
Unterklassenmethode:
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
}
Dekorationsmethode:
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
}
Ein letzter kurzer Punkt, den ich vergessen möchte, ist, dass Sie sich durch eine dieser Aktionen an eine spezifischere Implementierung binden, was bedeutet, dass Implementierungsentwickler so viel weniger Freiheit erhalten.