web-dev-qa-db-ja.com

Java Swingクラスをいつ拡張する必要がありますか?

継承の実装に関する私の現在の理解は、IS-A関係が存在する場合にのみクラスを拡張するべきであるということです。親クラスがさらに異なる機能を持つより具体的な子タイプを持つことができるが、親で抽象化された共通要素を共有する場合。

私のJava教授が私たちにそうすることを勧めているので、その理解に疑問を投げかけています。彼は、クラスで構築しているJSwingアプリケーションについて、

すべてのJSwingクラス(JFrameJButtonJTextBox、etc)を個別のカスタムクラスに拡張し、それらにGUI関連のカスタマイズを指定する必要があります(コンポーネントなど)サイズ、コンポーネントラベルなど)

これまでのところ良いが、彼はさらに、すべてのJButtonが独自のカスタム拡張クラスを持つべきであり、たとえそれらのラベルが唯一の区別要因であるとしても助言する。

たとえばGUIに2つのボタンOKおよびCancelがある場合。彼は以下のように拡張することを推奨します:

class OkayButton extends JButton{
    MainUI mui;
    public OkayButton(MainUI mui) {
        setSize(80,60);
        setText("Okay");
        this.mui = mui;
        mui.add(this);        
    }
}

class CancelButton extends JButton{
    MainUI mui;
    public CancelButton(MainUI mui) {
        setSize(80,60);
        setText("Cancel");
        this.mui = mui;
        mui.add(this);        
    }
}

ご覧のとおり、唯一の違いはsetText関数です。

それで、これは標準的な方法ですか?

ところで、これが議論されたコースはJavaでのベストプログラミングプラクティスと呼ばれます

[教授からの返信]

だから私は教授と問題について話し合い、答えで述べられたすべてのポイントを上げました。

彼の正当化は、GUI設計標準に従ってサブクラス化が再利用可能なコードを提供することです。たとえば、開発者が1つのウィンドウでカスタムのOkayおよびCancelボタンを使用した場合、他のウィンドウにも同じボタンを配置する方が簡単です。

私が思う理由はわかりますが、それでも継承を利用してコードを脆弱にしています。

その後、開発者がsetTextボタンでOkayを誤って呼び出し、変更する可能性があります。その場合、サブクラスは迷惑になります。

37
Paras

OkayButtonButtonが必要な場所では置換できないため、これは Liskov置換原理 に違反しています。たとえば、ボタンのラベルは自由に変更できます。しかし、OkayButtonを使用してこれを行うと、内部の不変条件に違反します。

これは、コードの再利用のための継承の典型的な誤用です。代わりにヘルパーメソッドを使用してください。

これを行わないもう1つの理由は、これが線形コードと同じことを達成するための複雑な方法にすぎないことです。

22
usr

それはあらゆる可能な方法で完全にひどいです。 mostで、ファクトリー関数を使用してJButtonを生成します。深刻な拡張が必要な​​場合にのみ、それらから継承する必要があります。

50
DeadMG

これはSwingコードを書くための非常に非標準的な方法です。通常、Swing UIコンポーネントのサブクラスを作成することはめったになく、最も一般的にはJFrame(子ウィンドウとイベントハンドラーを設定するため)ですが、そのサブクラス化は不要であり、多くの場合推奨されません。ボタンなどへのテキストのカスタマイズの提供は、通常、AbstractActionクラス(またはそれが提供する Actionインターフェース )を拡張することによって実行されます。これにより、テキスト、アイコン、およびその他の必要な視覚的なカスタマイズを提供し、それらを表す実際のコードにリンクできます。これは、表示する例よりもはるかに優れたUIコードの記述方法です。

(ところで、 Googleの学者はあなたが引用した論文を聞いたことがありません -より正確な参照がありますか?)

12
Jules

私見、ベストプログラミングプラクティスJavaはJoshua Blochの著書「Effective Java」で定義されています。教師があなたにOOP演習を提供していることは素晴らしいことであり、重要です他の人のプログラミングスタイルを読み書きする方法を学ぶためですが、Josh Blochの本以外では、ベストプラクティスについての意見はかなり異なります。

このクラスを拡張する場合は、継承を利用することもできます。 MyButtonクラスを作成して、共通コードを管理し、それを可変部分用にサブクラス化します。

class MyButton extends JButton{
    protected final MainUI mui;
    public MyButton(MainUI mui, String text) {
        setSize(80,60);
        setText(text);
        this.mui = mui;
        mui.add(this);        
    }
}

class OkayButton extends MyButton{
    public OkayButton(MainUI mui) {
        super(mui, "Okay");
    }
}

class CancelButton extends MyButton{
    public CancelButton(MainUI mui) {
        super(mui, "Cancel");
    }
}

これはいつ良いアイデアですか?作成したタイプをuseすると、!たとえば、ポップアップウィンドウを作成する関数があり、署名が次の場合:

public void showPopUp(String text, JButton ok, JButton cancel)

先ほど作成したタイプは、何の効果もありません。だが:

public void showPopUp(String text, OkButton ok, CancelButton cancel)

これで便利なものを作成しました。

  1. コンパイラーは、showPopUpがOkButtonとCancelButtonを受け取ることを確認します。誰かreadingコードは、この関数がどのように使用されることが意図されているかを知っています。これは、この種類のドキュメントが古くなるとコンパイル時エラーが発生するためです。これは大きなメリットです。タイプセーフティの利点に関する1つまたは2つの経験的研究では、人間のコード理解が唯一の定量化可能な利点であることがわかりました。

  2. これにより、関数に渡すボタンの順序を逆にするエラーも防止されます。これらのエラーを特定することは困難ですが、非常にまれであるため、これはマイナーな利点です。これはタイプセーフの販売に使用されましたが、特に有用であることが経験的に証明されていません。

  3. 関数の最初の形式は、任意の2つのボタンを表示するため、より柔軟です。場合によっては、使用するボタンの種類を制限するよりも優れています。任意のボタン機能をより多くの状況下でテストする必要があることを覚えておいてください。適切な種類のボタンを渡したときにのみ機能する場合は、任意の種類のボタンをとるふりをして、誰かに好意を払っていません。

オブジェクト指向プログラミングの問題は、カートを馬の前に置くことです。最初に関数を記述して、型を作成する価値があるかどうかを知るためにシグネチャを知る必要があります。ただし、Javaでは、関数のシグネチャを記述できるように、まず型を作成する必要があります。

このため、Clojureコードを記述して、最初に関数を記述することがいかに素晴らしいかを確認することができます。漸近的な複雑さを可能な限り考慮して、すばやくコーディングできます。Clojureの不変性の仮定は、Javaで型が行うのと同じくらいバグを防ぎます。

私はまだ大規模プロジェクトの静的型のファンであり、現在は関数を使用して 最初に関数を記述し、後でJavaで型に名前を付ける を許可しています。ちょっとした考え。

追伸muiポインタをfinalにしました-変更可能である必要はありません。

10
GlenPeterson

先生は、Swingコンポーネントを常に拡張して使用する必要があると教師が実際に信じていないことは間違いありません。きっと、これを例として使用して、クラスの拡張を練習するよう強制しているに違いない。実世界のベストプラクティスについてはあまり心配しません。

つまり、現実の世界では 継承よりも構成 です

「IS-A関係が存在する場合にのみクラスを拡張する」というルールは不完全です。 "...で終わる必要があり、クラスのデフォルトの動作を変更する必要があります"大きな太字で表示されます。

あなたの例はその基準に適合しません。テキストを設定するだけのためにextendクラスをJButtonする必要はありません。コンポーネントを追加するためだけにextendJFrameクラスを使用する必要はありません。デフォルトの実装を使用してこれらのことをうまく行うことができるので、継承を追加することは不必要な複雑さを追加するだけです。

別のクラスextendsのクラスを見つけたら、そのクラスはchangeingとは何なのかと思います。何も変更しない場合は、クラス全体を調べさせないでください。

あなたの質問に戻ります:Javaクラスを拡張する必要があるのはいつですか?クラスを拡張する本当に、本当に、本当に良い理由があるとき。

具体的な例を次に示します。カスタムペイントを行う1つの方法(ゲームやアニメーション、またはカスタムコンポーネントのみ)は、JPanelクラスを拡張することです。 (詳細は ここ です。)paintComponent()関数をオーバーライドする必要があるため、JPanelクラスを拡張します。これを行うことで、実際にクラスの動作を変更していますデフォルトのJPanel実装では、カスタムペイントを実行できません。

しかし、私が言ったように、あなたの教師はおそらくこれらの例を単にクラスの拡張を練習することを強制する言い訳として使用しています。

8
Kevin Workman