コンストラクターまたはセッターメソッドを使用しますか?


16

私はActionクラスを持っているUIコードで作業しています、このようなもの-

public class MyAction extends Action {
    public MyAction() {
        setText("My Action Text");
        setToolTip("My Action Tool tip");
        setImage("Some Image");
    }
}

このActionクラスが作成されたとき、Actionクラスはカスタマイズできないと想定されていました(ある意味で、そのテキスト、ツールチップ、またはイメージはコードのどこでも変更されません)。現在、コード内の特定の場所でアクションテキストを変更する必要があります。そのため、ハードコーディングされたアクションテキストをコンストラクターから削除し、引数として受け入れるように同僚に提案しました。以下のこのコードのようなもの-

public class MyAction extends Action {
    public MyAction(String actionText) {
        setText(actionText);
        setTooltip("My Action tool tip"); 
        setImage("My Image"); 
    }
}

ただし、setText()メソッドは基本クラスに属しているため、アクションインスタンスが作成された場所にアクションテキストを渡すために柔軟に使用できると考えています。そうすれば、既存のMyActionクラスを変更する必要はありません。したがって、彼のコードは次のようになります。

MyAction action = new MyAction(); //this creates action instance with the hardcoded text
action.setText("User required new action text"); //overwrite the existing text.

それが問題に対処する正しい方法であるかどうかはわかりません。上記の場合、ユーザーはとにかくテキストを変更しようとしていると思うので、アクションの構築中にユーザーを強制しないでください。元のコードで私が見る唯一の利点は、ユーザーがテキストの設定をあまり考えずにActionクラスを作成できることです。


1
使用している言語では、コンストラクタのオーバーロードが許可されていませんか?
マット

1
私はJavaを使用しているので、はい、できます。これはこれに対処する1つの方法になると思います
-zswap

2
事実の後にクラスメンバーを設定するパブリックな方法がない場合、クラスは事実上不変であることに注意してください。パブリックセッターを許可することで、クラスはmutableになり、不変性に依存している場合はこれを考慮する必要があります。
cbojar 14

オブジェクトを有効にするために設定する必要がある場合は、すべてのコンストラクターに入れてください...オプションであり(合理的なデフォルトがある)、不変性を気にしない場合は、セッターに入れてください。オブジェクトを無効な状態にインスタンス化すること、またはインスタンス化後に可能な限り無効な状態にすることは不可能です。
ビルK 14年

回答:


15

元のコードで私が見る唯一の利点は、ユーザーがテキストの設定をあまり考えずにActionクラスを作成できることです。

それは実際には利点ではありません。ほとんどの目的にとっては欠点であり、残りのケースでは私はそれをネクタイと呼びます。誰かが構築後にsetText()の呼び出しを忘れた場合はどうなりますか?異常な場合、おそらくエラーハンドラーの場合はどうでしょうか。本当にテキストを強制的に設定したい場合は、コンパイル時にエラーが実際に致命的であるため、コンパイル時に強制する必要があります。実行時に発生することは、実行される特定のコードパスに依存します。

次の2つの明確なパスがあります。

  1. 提案どおり、コンストラクターパラメーターを使用します。本当にしたい場合はnull、空の文字列を渡すか、空の文字列を渡すことができますが、テキストを割り当てていないという事実は暗黙ではなく明示的です。nullパラメーターの存在を確認し、おそらくそこに何らかの考えがあったことを確認するのは簡単ですが、メソッド呼び出しの欠如を確認し、そのような欠如が意図的なものであるかどうかを判断するのは容易ではありません。このような単純なケースの場合、これはおそらく私が取るアプローチです。
  2. ファクトリ/ビルダーパターンを使用します。これはこのような単純なシナリオではやり過ぎかもしれませんが、より一般的な場合、任意の数のパラメーターを設定し、オブジェクトのインスタンス化の前または中に前提条件を確認できるため、非常に柔軟です(オブジェクトの構築が大きな操作である場合/または、クラスを複数の方法で使用できる場合、これは大きな利点になります)。特にJavaでは一般的なイディオムであり、使用している言語とフレームワークで確立されたパターンに従うことは、めったに悪いことではありません。

10

コンストラクターのオーバーロードは、ここでは単純で簡単な解決策です。

public class MyAction extends Action {
    public MyAction(String actionText) {
        setText(actionText);
        setTooltip("My Action tool tip"); 
        setImage("My Image"); 
    }
    public MyAction() {
        this("My Action Text");
    }
}

.setText後で呼び出すよりも優れています。この方法では、何も上書きする必要がなくactionText、最初から意図したものになります。

コードが進化し、さらに柔軟性が必要になると(確かに起こります)、別の回答で提案されたファクトリー/ビルダーパターンの恩恵を受けるでしょう。


2番目のプロパティをカスタマイズする場合はどうなりますか?
ケビンクライン

3
2番目、3番目、..プロパティについても同じ手法を適用できますが、カスタマイズするプロパティが多いほど、扱いにくくなります。ある時点で、ファクトリー/ビルダーのパターンを実装する方が理にかなっています。また、@ michael-kjorlingが彼の答えで言っています。
ジャノス

6

流fluentな 'setText'メソッドを追加します。

public class MyAction ... {
  ...
  public MyAction setText(String text) { ... ; return this; }
}

MyAction a = new MyAction().setText("xxx");

それよりも明確なものは何でしょうか?別のカスタマイズ可能なプロパティを追加する場合、問題ありません。


+1、私は同意し、より多くのプロパティを補完する別の答えを追加しました。例として、複数の単一のプロパティがある場合、流れるようなAPIを理解する方が明確だと思います。
マチャド

特に不変オブジェクトを生成するビルダーにとって、流なインターフェイスが好きです!パラメータが多いほど、これはうまく機能します。しかし、この質問の具体例を見ると、setText()MyActionが継承するActionクラスで定義されていると思います。おそらくvoid戻り値型が既にあります。
グレンペターソン

1

同じようにケビン・クラインは彼の答えに言った、私が移動するための方法を作成することだと思う流暢APIを。使用できるプロパティが複数ある場合は、流れるようなAPIがより適切に機能することを付け加えます。

それはあなたのコードを読みやすく、そして、私の視点から、より簡単となりますAHAM、書き込みに「セクシー」。

あなたの場合、これは次のようになります(タイプミスは申し訳ありませんが、最後のJavaプログラムを書いてから1年が経ちました):

 public class MyAction extends Action {
    private String _text     = "";
    private String _tooltip  = "";
    private String _imageUrl = "";

    public MyAction()
    {
       // nothing to do here.
    }

    public MyAction text(string value)
    {
       this._text = value;
       return this;
    }

    public MyAction tooltip(string value)
    {
       this._tooltip = value;
       return this;
    }

    public MyAction image(string value)
    {
       this._imageUrl = value;
       return this;
    }
}

そして、使用方法は次のようになります。

MyAction action = new MyAction()
    .text("My Action Text")
    .tooltip("My Action Tool tip")
    .image("Some Image");

悪い考え、彼らがテキストや重要なものを設定するのを忘れたらどうなるか。
プラーハー

1

コンストラクターまたはビルダーを使用するためのアドバイスは一般的には問題ありませんが、私の経験では、アクションのいくつかの重要なポイントを見逃しています。

  1. おそらく国際化が必要
  2. マーケティングは土壇場で変化する可能性があります。

名前、ツールチップ、アイコンなどをプロパティファイル、XMLなどから読み取ることを強くお勧めします。たとえば、ファイルを開くアクションの場合、プロパティを渡すことができます。

File.open.name=Open
File.open.tooltip=Open a file
File.open.icon=somedir/open.jpg

これは、フランス語に翻訳したり、新しい優れたアイコンなどを試すのに非常に簡単な形式です。プログラマーの時間や再コンパイルなしで。

これは大まかな概要であり、多くは読者に任されています...国際化の他の例を探してください。


0

コンストラクター内でsetText(actionText)またはsetTooltip( "My action tool tip")を呼び出しても意味がありません。対応するフィールドを単純に直接初期化する方が簡単です(そしてパフォーマンスが向上します)。

    public MyAction(String actionText) {
        this.actionText = actionText;
    }

MyAction対応オブジェクトの存続期間中にactionTextを変更する場合は、setterメソッドを配置する必要があります。そうでない場合は、セッターメソッドを提供せずにコンストラクターでのみフィールドを初期化します。

ツールチップと画像は定数であるため、定数として扱います。フィールドがあります:

private (or even public) final static String TOOLTIP = "My Action Tooltip";

実際、一般的なオブジェクト(厳密にはデータ構造を表すBeanやオブジェクトではない)を設計するときは、セッターとゲッターを提供するのは悪い考えです。


4
中途有能なコンパイラーとJITterはsetText()などの呼び出しをインライン化する必要があります。そのため、割り当てを行う関数呼び出しと、関数呼び出しの代わりにその割り当てを行うことのパフォーマンスの違いは、ほとんど無視できます。ゼロではありません。
CVn

0

これは、ジェネリックアクションクラス(Employee、Departmentの更新に使用されるupdateなど)を作成する場合に当てはまると思います。それはすべてシナリオに依存します。特定のアクション(従業員の更新など)クラス(アプリケーションの多くの場所で使用-従業員の更新)がアプリケーション内のすべての場所で同じテキスト、ツールチップ、画像を保持する意図で作成された場合(一貫性の観点から)。そのため、テキスト、ツールチップ、および画像に対してハードコーディングを行い、デフォルトのテキスト、ツールチップ、および画像を提供できます。これらをカスタマイズするには、柔軟性を高めるために、対応するセッターメソッドが必要です。変更する必要があるのは10%だけであることに留意してください。ユーザーから毎回アクションテキストを取得すると、同じアクションに対して毎回異なるテキストが発生する可能性があります。「Update Emp」、「Update Employee」、「Change Employee」、または「Edit Employee」など。


オーバーロードされたコンストラクターでも問題を解決できると思います。すべての「10%」の場合、最初にデフォルトのテキストでアクションを作成し、次に「setText()」メソッドを使用してアクションのテキストを変更するためです。アクションの作成中に適切なテキストを設定してください。
-zswap

0

インスタンスがどのように使用されるかを考え、ユーザーにそれらのインスタンスを適切な方法で、または少なくとも最善の方法で使用するように導く、または強制するソリューションを使用します。このクラスを使用するプログラマーには、心配して考えなければならないことがたくさんあります。このクラスはリストに追加しないでください。

たとえば、MyActionクラスが構築(および場合によっては他の初期化)後に不変であると想定される場合、セッターメソッドを使用しないでください。ほとんどの場合、デフォルトの「My Action Text」を使用する場合、パラメーターなしのコンストラクターと、オプションのテキストを許可するコンストラクターが必要です。これで、ユーザーは90%の時間クラスを正しく使用することを考える必要がなくなりました。ユーザーは通常ならばべきテキストにいくつかの考えを与えるために、パラメータなしのコンストラクタをスキップします。ユーザーは必要なときに考えることを余儀なくされ、必要なステップを見落とすことができなくなりました。

場合はMyAction、インスタンスがフル建設後に変更可能である必要があり、あなたはテキストのセッターを必要としています。コンストラクターでの値の設定をスキップすることは魅力的です(DRY原則-「自分自身を繰り返さない」)。通常、デフォルト値で十分な場合は十分です。しかし、そうでない場合、コンストラクターでテキストを要求すると、ユーザーはいつすべきかを考えるように強制されます。

これらのユーザーは愚かではないことに注意してください。心配するべき本当の問題が多すぎます。クラスの「インターフェース」について考えることで、クラスが実際の問題にならず、不必要な問題にならないようにすることができます。


0

次の提案ソリューションでは、スーパークラスは抽象クラスであり、3つのメンバーすべてがデフォルト値に設定されています。

サブクラスには異なるコンストラクターがあるため、プログラマはインスタンス化できます。

最初のコンストラクターが使用される場合、すべてのメンバーはデフォルト値を持ちます。

2番目のコンストラクターを使用する場合、actionTextメンバーに初期値を与え、他の2つのメンバーはデフォルト値のままにします...

3番目のコンストラクターを使用する場合、actionTextおよびtoolTipの新しい値でインスタンス化し、imageURlをデフォルト値のままにします...

等々。

public abstract class Action {
    protected String text = "Default action text";
    protected String toolTip = "Default action tool tip";
    protected String imageURl = "http://myserver.com/images/default.png";

    .... rest of code, I guess setters and getters
}

public class MyAction extends Action {


    public MyAction() {

    }

    public MyAction(String actionText) {
        setText(actionText);
    }

    public MyAction(String actionText, String toolTip_) {
        setText(actionText);
        setToolTip(toolTip_);   
    }

    public MyAction(String actionText, String toolTip_; String imageURL_) {
        setText(actionText);
        setToolTip(toolTip_);
        setImageURL(imageURL_);
    }


}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.