これは一般的な問題ではありません。対処する必要がある問題は、インターフェースの期待です。副作用のないインターフェイスの動作と、副作用を許可するインターフェイスを組み合わせています。
このことを考慮:
public class CommandMonitor {
public static void main(String[] args) {
Command<?> sec = new SideEffectCommand();
reportResults(sec);
}
public static void reportResults(Command<?> cmd){
final Object results = cmd.execute("arg");
if (results != null ) {
System.out.println(results.getClass());
}
}
}
<Void>
テンプレート型として使用することに問題はありませんが、「コマンド<T>
」の実装と混合できるようにすることは、インターフェースの一部のクライアントが void の結果を予期しない可能性があることを意味します。インターフェイスを変更しないと、実装によって予期しない結果が生じる可能性があります。
Collection クラスを使用して一連のデータを渡す場合、私のチームは、構文的には問題ありませんが、決してnull を返さないことに同意しました。問題は、戻り値を使用するクラスが、NPE を防ぐために常に void をチェックする必要があることでした。上記のコードを使用すると、どこにでも次のように表示されます。
if (results != null ){
実装が実際にオブジェクトを持っているか、null であるかを知る方法があるためです。特定のケースについては、実装に精通しているため、必ず知っているはずです。しかし、それらを集約し始めるか、コーディングの範囲を超えるとすぐに (ライブラリ、将来のメンテナンスなどとして使用されます)、ヌルの問題が発生します。
次に、これを試しました:
public class SideEffectCommand implements Command<String> {
@Override
public String execute(String... args) {
return "Side Effect";
}
}
public class NoSideEffectCommand implements Command<Void>{
@Override
public Void execute(String... args) {
return null;
}
}
public class CommandMonitor {
public static void main(String[] args) {
Command<?> sec = new SideEffectCommand();
Command<?> nsec = new NoSideEffectCommand();
reportResults(sec.execute("args"));
reportResults(nsec.execute("args")); //Problem Child
}
public static void reportResults(Object results){
System.out.println(results.getClass());
}
public static void reportResults(Void results){
System.out.println("Got nothing for you.");
}
}
reportResults への 2 回目の呼び出しは、(もちろん) オブジェクトを期待するバージョンをまだ呼び出していたため、オーバーロードは機能しませんでした。に変更することを検討しました
public static void reportResults(String results){
しかし、これは問題の根本を示しており、クライアント コードは実装の詳細を知る必要があります。インターフェイスは、可能であればコードの依存関係を分離するのに役立つはずです。この時点でそれらを追加するのは悪い設計のようです。
要するに、コマンドに副作用が予想される場合を明確にする設計を使用し、一連のコマンド (つまり、未知のコマンドの配列) をどのように処理するかを考える必要があるということです。
これは、漏れやすい抽象化のケースである可能性があります。