web-dev-qa-db-ja.com

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

私は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クラスを作成できることです。

16
zswap

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

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

次の2つの明確な道筋が見えます。

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

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

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は最初から意図したものにすることができます。

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

10
janos

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

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

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

それより何がはっきりしているのでしょうか?別のカスタマイズ可能なプロパティを追加することにした場合は、問題ありません。

6
kevin cline

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

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

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

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

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

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

1
user949300

kevin cline が彼の回答で言ったように、私はfluent API。使用できるプロパティが複数ある場合、Fluent 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
Machado

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

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

フル構築後にMyActionインスタンスを変更可能にする必要がある場合は、テキストのセッターが必要です。コンストラクターで値の設定をスキップするのは魅力的です(DRYの原則-"Do n't Repeat Yourself")。また、デフォルト値で通常十分であれば、私はそれで十分です。しかし、そうでない場合は、コンストラクター内のテキストを要求することで、ユーザーはいつ必要なのかを考える必要があります。

これらのユーザーダムではないであることに注意してください。彼らは心配するほど多くの実際の問題を抱えています。クラスの「インターフェース」を考えることで、それが本当の問題になることを防ぐことができます-そして不必要な問題。

0
RalphChapin

次の提案されたソリューションでは、スーパークラスは抽象的で、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_);
    }


}
0

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

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

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

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

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

実際、一般的なオブジェクト(Beanまたは厳密にデータ構造を表すオブジェクトではない)を設計する場合、セッターとゲッターはカプセル化の一種であるため、これらを提供することはお勧めできません。

0
m3th0dman

これは、ジェネリックアクションクラス(たとえば、従業員、部門などの更新に使用される更新)を作成する場合に当てはまると思います。それはすべてシナリオに依存します。特定のアクション(従業員の更新など)クラス(アプリケーションの多くの場所で使用される-従業員の更新)が作成され、アプリケーション内のすべての場所で同じテキスト、ツールチップ、および画像が維持されるようにする場合(整合性の観点から)。したがって、テキスト、ツールチップ、および画像に対してハードコーディングを行って、デフォルトのテキスト、ツールチップ、および画像を提供できます。柔軟性をさらに高めるために、これらをカスタマイズするには、対応するセッターメソッドが必要です。変更する必要がある場所は10%のみであることを覚えておいてください。ユーザーから毎回アクションテキストを取得すると、同じアクションに対して毎回異なるテキストが発生する可能性があります。 「従業員の更新」、「従業員の更新」、「従業員の変更」、「従業員の編集」のように。確かではありませんが、これがユーザーに混乱を引き起こし、これが何か別のものであると考える可能性があります。

0
Sandy