質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.64%

インターフェイス内に定数は持つべきではない?

解決済

回答 5

投稿

  • 評価
  • クリップ 1
  • VIEW 2,738
退会済みユーザー

退会済みユーザー

定数だけ定義されたインターフェイスではなく、インターフェイス内に定数は定義すべきではないのでしょうか?
インターフェイス内に定数を定義すると、SonarQubeで指摘されてしまいます。
直訳だと「"Effective Java"によると定数インターフェースパターンは、インターフェースの貧弱な使用です。」と表示されます。

ある計算を行うクラスと、そのインターフェイスがあります。
この計算クラスは、正しく計算結果を返すこともあれば、無効値を返すこともあります。
使用する側はDI注入により、インターフェイスから計算メソッドを呼び出します。

無効値を定数としてどこかに定義する必要があるのですが、インターフェイス内に定義するとSonarQubeで指摘されてしまのでenumや定数クラスを別途用意しなければなりません。
しかし、そのように修正すると、インターフェイスを使用する側は定数クラスをいちいちインポートして使用しなけれなりません。
計算クラスを使用する側はインターフェイスさえ知っていればよい状態にしたいのですが、不可能でしょうか?
もしくはそのような実装は推奨されないのでしょうか?

interface ICalculator{
  int INVALID_VALUE = -999; //Effective Javaに準拠していないと言われ怒られる
  int calculate();
}

class Calculator implements ICalculator {
  @Override
  int calculate() {
    //計算結果を返す。無効値を返すこともある。
  }
}

class Main {
  //使用する側

  //DI注入によりICalculatorの実体が渡ってくる
  private final ICalculator calculator;

  void main() {
    int result = this.calculator.calculate();
    //resultに無効値が返ってくることもあるので、無効値の定数をどこかに定義したい。
    //インターフェイスに定義すると怒られる。たかが1個の定数を定義するためにenumや定数クラスを作りたくない。
    //実体クラス側に定数を定義すると、DI注入の意味がないし、ICalculatorの実体クラスが増える度に定数を定義しなければならない。
  }
}
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 5

+2

maisumakunさんの回答とコメントがかみ合ってないですが・・・
変換ってボクシングされるから、ほとんど気にしなくてよいのでは?

質問者さんがやりたいこと?

  private final ICalculator calculator;

  void main() {
    int result = this.calculator.calculate();
    if(ICalculator.INVALID_VALUE != result) {
    // 有効な値がかえってきたときの処理
    } else {
    // 無効な値がかえってきたときの処理
    }
  }

maisumakunさんの回答

  private final ICalculator calculator;

  void main() {
    try {
        int result = this.calculator.calculate();
        // 有効な値がかえってきたときの処理
    } catch (InvalidCalcuateException e) {
        // 無効な値がかえってきたときの処理
    }
  }


そもそも、無効値は無効値として格納したいのであれば、そもそも判定とかも必要ないので、

 //resultに無効値が返ってくることもあるので、無効値の定数をどこかに定義したい。

の部分が意味不明ですけど・・・

個人的にはインターフェースに定数作るのはアリなのですが・・・
解決するなら、こんな感じ?

  private final ICalculator calculator;

  void main() {
    int result;
    try {
        result = this.calculator.calculate();
        // 有効な値がかえってきたときの処理
    } catch (InvalidCalcuateException e) {
        // 無効な値がかえってきたときの処理
        Logger.error("無効な値が返ってきた");
        result = 定数クラス.無効な値;
    }
    // DB格納処理
  }

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

+1

//計算結果を返す。無効値を返すこともある。

そもそも論ですが、たまたま計算結果が「無効値」と一致してしまったらどうするのでしょうか。そして、このメソッドを使う側でうっかり無効値のハンドリングを忘れてしまったら、どうなるでしょうか。intの範囲内でそれを実現しようとする設計が良くないです。

異常時には、例外を投げましょう。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/11/29 12:03

    計算結果の無効値は無効値として格納したいのです。
    例としてintを挙げましたが、実際は計算結果として100%あり得ない数値を無効値として返却しています。(DBに計算に使用しているデータがあるが、その範囲を超えるため)
    参照型を使用すると
    ・値を格納する際にプリミティブに変換しなければならない
    ・使用するメモリ領域が大きくなる
    ・リファクタリングの範囲が広がる
    などの問題があるので、nullを返す方法は取れないです。

    キャンセル

  • 2018/11/29 18:44

    よこからすいません。
    メモリの問題はそんな大差ないと思いますよ。よっぽどのことがない限り無視していいと思われます。
    プリミティブに変換 は 、 java はボクシング というのがあり、暗黙的に変換されます。

    キャンセル

  • 2018/11/29 23:30

    maisumakunさん回答の1~2番目のコメント欄に書いておられることに気づけずに回答をつけてしまいました。失礼しました。ボケてました...

    キャンセル

+1

解決済みですが・・・

インターフェースの主な目的は「機能を提供する手段としてのメソッド」を規定することで、メソッドの規定の基本はシグナチャーなので、シグナチャーだけで規定を完結する(つまり型だけによって規定する)ために、戻り値の型をintではなく次のようにするという選択もあると思います。

// (A)
interface ICalculator {
  Optional<Integer> calculate();
}

// または

// (B)
interface ICalculatorB {
  OptionalInt calculate();
}

// もっとさぼるなら

// (C)
interface ICalculatorC {
  Integer calculate();
}

intでは表現しきれない結果を無理にint型にしたためにINVALID_VALUEなんて特殊な値を定義しなくてはならないわけで、そんな値を定義しなくてもよいように「あり得る状況を全て表現できる型」を用いるわけです。


蛇足:
(C)は特殊状況を悪名高き(?)nullで表現するので、チェックを忘れてもコンパイル時に気づけないことも多く、実行時にNPEに遭遇なんてことにもなりますが、(A), (B)ではチェックし忘れの可能性は排除できます。ただし(A)ならmapを用いて

ICalculator calculator = ...;
String s = calculator.calculate()
           .map(v -> String.format("%,7d", v))
           .orElse("---,---");
System.out.println(s);


などと直接的なメソッドチェーンが書けますが(B)だとなぜかOptionalIntにmapが定義されてないため上記のようには書けません。個人的にはそうまで低水準な機能を設計しているのでないかぎりprimitiveにこだわらずに(A)を用いたほうが好みです。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/11/29 23:32

    自分の回答はmaisumakunさんがご自身の回答のコメントに書いておられることと同様の話です。わざわざ回答するまでもありませんでした。失礼しました。orz

    キャンセル

checkベストアンサー

0

インターフェイスの本来の役割は、呼び出す側が呼び出せるメソッドの構成を呼び出される側に強制することです。参考

一方定数とは、プログラム全体に影響を及ぼすような値を1つの変数名で宣言してプログラム全体でその変数を用いることにより、外部環境の都合に合わせてその値だけを変更することでプログラムの動作を変更することがその目的だと思います。(私はそう理解しています。)
例えば日付を扱うプログラムで、「休日」の設定を定数にしている場合、休日は年度ごとに異なりますから、年度が変わるごとに定数のみを変更することでシステムを動作させたいですよね。

そう考えると、個人的にもインターフェースに定数を用いることは得策とは思いません。
「無効値」が将来的に変更されることがあるのかどうかは不明ですが、例えば「無効値」を「0」から「9」に変更したとして、過去に作成されたインターフェースを用いてコンパイルされたクラスは全て「無効値」を「0」と認識しているわけですから、全てコンパイルし直さないと呼び出し側から見て使えないことになります。

今回のようなケースでしたら、定数を保持する外部ファイルを作成してその定数用外部ファイルを都度読み込んで計算クラスを動作させるか、「無効値」が将来にわたって変わらないのであれば、固定値を返却しても良いのではないかと思います。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

0

ちょうど https://code.i-harness.com/ja/q/289509 にもいい議論がありますので、参考にされても良いでしょう。

個人的には、そのインタフェース定数は、インタフェースを実装したクラスないしはサブクラスでのみ使われるよう制約を付けていれば良いかと。

なんでもかんでも定数定義用のEnumないしはパッケージに入れて、用途が直接見えにくくなるのは避けたい、という意図があります。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.64%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る