過去10年ほどの間、私はJavaユーティリティクラスに以下のパターンを使用してきました。このクラスには静的メソッドとフィールドのみが含まれ、final
と宣言されています。拡張されておらず、private
コンストラクターがあるため、インスタンス化できません。
public final class SomeUtilityClass {
public static final String SOME_CONSTANT = "Some constant";
private SomeUtilityClass() {}
public static Object someUtilityMethod(Object someParameter) {
/* ... */
return null;
}
}
さて、 インターフェイスの静的メソッド in Java 8)の導入により、最近、ユーティリティインターフェイスパターンを使用していることに気付きました。
public interface SomeUtilityInterface {
String SOME_CONSTANT = "Some constant";
static Object someUtilityMethod(Object someParameter) {
/* ... */
return null;
}
}
これにより、コンストラクターと、インターフェイスに暗黙的に含まれている多くのキーワード(public
、static
、final
)を取り除くことができます。
このアプローチの欠点はありますか?ユーティリティインターフェイスを介してユーティリティクラスを使用することに利点はありますか?
コンスタントインターフェイスパターンをアンチパターンとして作成した人に基づいて、クライアントにインターフェイスを実装するつもりはありませんが、それでも可能であり、おそらく簡単であり、許可されるべきではありません:
APIは使いやすく、誤用しにくいものでなければなりません。簡単なことをするのは簡単なはずです。複雑なことをすることが可能です。 そして間違ったことをすることは不可能、または少なくとも難しい。
以下に述べるように、それは本当にターゲットオーディエンスに依存します
使いやすいデザインパターンの多くは、多くの批判を受けます(コンテキストパターン、シングルトンパターン、コンスタントインターフェイスパターン)。デメテルの法則などの設計原則でさえ、冗長すぎると批判されます。
言いたくないのですが、こういう決断は意見に基づいています。コンテキストパターンはアンチパターンと見なされますが、SpringやAndroid SDKなどの主流のフレームワークで明らかです。環境だけでなく、ターゲットオーディエンス。
私が見つけることができる主な欠点は、 Constant Interface wiki の「欠点」の下に3番目のリストとしてリストされています。
将来のリリースでバイナリコードの互換性が必要な場合、定数インターフェイスは、従来のインターフェイスとして使用されていなくても、永久にインターフェイスのままである必要があります(クラスに変換できません)センス。
「ねえ、これは実際には契約ではないので、より強力な設計を実施したい」と思った場合、それを変更することはできません。しかし、私が言ったように、それはあなた次第です。多分あなたは将来それを変えることを気にしないでしょう。
その上、@ TagirValeevで言及されているようにコードを明確にします。インターフェイスには実装する意図があります。提供しているAPIを誰かに実装させたくない場合は、実装可能にしないでください。しかし、これは「ターゲットオーディエンス」ステートメントを中心に展開していると思います。うそをつくつもりはありません。私は冗長性の低い基盤であなたと一緒にいますが、それは私のコードが誰のためのものかによって異なります。レビューされる可能性のあるコードに一定のインターフェイスを使用したくないでしょう。
誰かがそれを実装することを期待する場合にのみ、インターフェースを使用する必要があります。例えば、 Java.util.stream.Stream
インターフェイスには、Java 8より前のStreams
またはStreamUtils
クラスに配置できる静的メソッドがたくさんあります。ただし、これは有効なインターフェイスです。非静的メソッドも同様に実装できます。Java.util.Comparable
は別の例です。そこにあるすべての静的メソッドは、インターフェースをサポートするだけです。ユーザーがパブリックインターフェイスを実装することを禁止することはできませんが、ユーティリティクラスの場合、ユーザーがそれをインスタンス化することを禁止することができます。したがって、コードを明確にするために、実装することを意図していない限り、インターフェイスを使用しないことをお勧めします。
@ saka1029の回答に関するメモ。同じインターフェイスでヘルパープライベートメソッドと定数を定義できないのは事実ですが、必要な実装関連のものがすべて含まれるMyInterfaceHelper
のような同じパッケージでパッケージプライベートクラスを作成することは問題ではありません。一般に、パッケージプライベートクラスは、実装の詳細を外界から隠すのに適しています。
インターフェイスは使用しないでください。インターフェイスにプライベート定数と静的初期化子を含めることはできません。
public class Utility {
private Utility() {}
public static final Map<String, Integer> MAP_CONSTANT;
static {
Map<String, Integer> map = new HashMap<>();
map.put("zero", 0);
map.put("one", 1);
map.put("three", 3);
MAP_CONSTANT = Collections.unmodifiableMap(map);
}
private static String PRIVATE_CONSTANT = "Hello, ";
public static String hello(String name) {
return PRIVATE_CONSTANT + name;
}
}
私はそれがうまくいくと思います。明示的に指定していなくても、変数SOME_CONSTANTはデフォルトでSomeUtilityInterfaceの静的finalになると思います。それで、それはユーティリティとして機能しますが、すべてのメンバー変数が最終である必要がある通常のクラスでは発生しないいくつかの可変性の問題がありませんか?それがデフォルトメソッドの特定の実装の問題でない限り、私は問題を考えることはできません。