クラスのメソッドは独自のゲッターとセッターを呼び出す必要がありますか?


53

私が働いている場所では、このようなことをするクラスがたくさんあります。

public class ClassThatCallsItsOwnGettersAndSetters {

    private String field;

    public String getField() {
        return field;
    }

    public void setField(String field) {
        this.field = field;
    }

    public void methodWithLogic() {
        setField("value");
        //do stuff
        String localField = getField();
        //do stuff with "localField"
    }
}

これを最初から書いた場合methodWithLogic()、代わりに次のように書いたでしょう。

public class ClassThatUsesItsOwnFields {

    private String field;

    public String getField() {
        return field;
    }

    public void setField(String field) {
        this.field = field;
    }

    public void methodWithLogic() {
        field = "value";
        //do stuff            
        //do stuff with "field"
    }
}

クラスが独自のゲッターとセッターを呼び出すと、コードが読みにくくなります。私にとっては、このメソッド呼び出しでは複雑なロジックが発生していることをほぼ暗示していますが、このケースではほとんど発生していません。なじみのないコードをデバッグしているとき、バグがそのメソッドの副作用ではないと言うのは誰ですか?言い換えれば、コードを理解するために多くのサイドトリップをするようになります。

最初の方法には利点がありますか?最初の方法は実際には良いですか?


なじみのないコードをデバッグしているとき、バグがそのメソッドの副作用ではないと言うのは誰ですか?ユニットテスト。:)
ジョシュアテイラー14

1
内部コードでゲッター/セッターを使用すると、デバッガーを使用してコードにブレークポイントを作成する場合に、コードにブレークポイントを作成できます。また、値の設定/取得で問題が発生した場合、無効なアクセスではなく、そのメソッドが原因であることを確認できます。全体として、より一貫性のあるコードを提供します。
callyalater

回答:


46

どちらが良いか悪いかは言いませんが、それはあなたの状況に一部依存するからです(私の意見では)。しかし、ゲッターとセッターは後で実装を変更する可能性があり、それらをバイパスするとそのロジックがスキップされることを考慮してください。

たとえば、後でいくつかのセッターフィールドに「ダーティ」フラグを追加するとどうなりますか?内部コードでセッターを呼び出すことにより、他のコードを変更せずにダーティフラグを設定します。多くの場合、これは良いことです。


しかし、バックエンドでデータを初期化しているときに、ダーティと解釈されたくない場合はどうでしょう。setField()最初から呼び出した場合は、バグを導入したばかりです。
ダニエルカプラン

6
そうです、それは私がそれが状況に部分的に依存すると言う理由です。データの初期化はセッターをスキップする必要がありますが、他の論理コードはスキップしないでください。適切なルールを選択し、それに従ってください。
マットS

3
@DanielKaplan:ポイントは、ゲッターとセッターを呼び出すことはほとんどの場合正しいことであり、特定の状況ではそうではないということです。ただし、実際のコードでは、既存のgetter / setter-implementationを後で変更して意図的な副作用を導入する場合、ほとんどの場合、クラス内のgetterまたはsetterへのすべての呼び出しをチェックする必要があります。また、フィールド。そのため、クラスをできるだけ小さくする必要があります。
Doc Brown

33

セッターを直接呼び出すこと自体は問題ではありません。問題は本当にあるはずです:なぜ私たちのコードはどこにでもセッターを持っているのですか?

可変クラスは、特にクラス自体が独自の状態を管理する場合、危険なゲームです。それらのセッターのうち、本当に存在する必要があるものの数を考慮してください。コンストラクタにいくつ設定して、クラス自体で完全にカプセル化できますか?

フィールドを外部で設定可能にする必要がある場合は、ロジックを持つメソッドをそこに配置する必要があるかどうかを自問してください。クラス自体は本当に単なるデータ/状態クラスですか?このクラスには複数の責任がありますか?

誤解しないでください。これで問題ない場合があります。ロジックを持つクラスのセッターを完全に削除する必要があると言っているわけではありません。しかし、これらは規則ではなく例外であるべきです。そして、これらのいくつかのケースでは、どちらに直接アクセスするかが明らかであるはずです。


1
私はこの考え方に完全に同意/実践しています。また、あなたが私の質問よりも実際に重要な点を提起すると思います。とはいえ、元の質問に直接答えているとは思わない。「物事の壮大な計画では、あなたの質問は重要ではない」と言っているのでなければ。これも真実かもしれません:)
ダニエルカプラン

@DanielKaplan:まさにそう言っています。:)私はこれで答えとコメントの間で引き裂かれましたが、私は本当に大きな質問を求めない正しい答えがあるとは感じません。
pdr

9

はい、クラスのメソッドはゲッターとセッターを呼び出す必要があります。ゲッターとセッターを書くことの全体のポイントは、将来の校正です。すべてのプロパティをフィールドにして、クラスのユーザーにデータを直接公開できます。ゲッターとセッターを作成する理由は、必ずしも複雑なロジックがあるからではありませんが、追加する必要がある場合にインターフェースが将来壊れないようにするためです。


1
最初の文と段落の残りの部分との関係はわかりません。最初の文は、クラスが独自のゲッターとセッターを呼び出す必要があることを示しています。残りの段落では、クライアントコード(クラスを使用するコード)がフィールドに直接アクセスする代わりにゲッターとセッターを使用する理由を説明します。しかし、クラスがそれ自身のフィールドに直接アクセスしてはならない理由がどのようになっているのかわかりません。
ダニエルカプラン

1
@DanielKaplan私のポイントは、理由はまったく同じだということです。セッターロジックを後で追加すると、外部コードと同じくらい(潜在的に)内部コードに影響します。
マイケル

2
@Michael:多くの場合、クラスが独自のゲッター/セッターを使用しない理由があります。1つの一般的なシナリオは、フォームのセッターが多数あるsomeField=newValue; refresh(); 場合です。メソッドが複数のフィールドの設定を許可する場合、セッターを呼び出してこれらのフィールドを書き込むと、冗長な「更新」操作が発生します。すべてのフィールドを書き込んでからrefresh()1回呼び出すと、より効率的で滑らかな操作が得られます。
supercat

6

はい、一言で質問に答えてください。

クラスが独自のゲッターとセッターを呼び出すと、拡張性が追加され、将来のコードのより良いベースが提供されます。

次のようなものがあるとしましょう:

public class Vehicle
{
    private int year;
    private String make;

    public Vehicle(int year, String make)
    {
        setYear(year);
        setMake(make);
    }

    public void setYear(int year)
    {
        this.year = year;
    }

    public void setMake(String make)
    {
        this.make = make;
    }
}

現在、yearおよびsetのセッターを呼び出しても機能が追加されない場合がありますが、セッターに入力検証などを追加する場合はどうでしょうか。

public class Vehicle
{
    private int year;
    private String make;

    public Vehicle(int year, String make)
    {
        setYear(year);
        setMake(make);
    }

    public void setYear(int year)
    {
        if(year > 0)
        {
            this.year = year;
        }
        else
        {
            System.out.println(year + " is not a valid year!");
        }
    }

    public void setMake(String make)
    {
        this.make = make;
    }
}

5

言及されていないことの1つは、ゲッターとセッター(すべてのメソッドとして)がJavaで仮想であることです。これにより、コードで常にそれらを使用することに別の機能が追加されます。別のユーザーがクラスを拡張し、ゲッターとセッターを上書きする可能性があります。基本クラスは、独自のクラスではなくサブクラスのデータを使用します。仮想関数を明示的にマークする言語では、これを使用して実行できる関数を予測および宣言できるため、これははるかに便利です。Javaでは、これは常に意識しなければならないものです。必要な動作であれば、コードでそれらを使用することは良いことです。


3

パブリックインターフェイスによって提供される何かを達成しようとしている場合は、ゲッター/セッターを使用します。

ただし、クラスの所有者/開発者は(もちろんクラス内から)コードのプライベートセクションにアクセスできますが、危険を軽減する責任もあります。

そのため、内部リストを反復処理するゲッターがあり、イテレータをインクリメントせずに現在の値を取得したい場合があります。この場合、プライベート変数を使用します。

public class MyClass
{
    private int i;
    private List<string> list;
    public string getNextString()
    {
        i++;
        return list[i];
    }

    private void getString()
    {
        // Do not increment
        string currentString = list[i];

        // Increment
        string nextString = getNextString();
    }
}

その理由を教えてください。
ダニエルカプラン

1

はい。ゲッターとセッターは状態を表すので、この質問に向きを変えます-必要がない限り、同じ方法でオブジェクトの状態を変更する複数の方法を追跡する必要がありますか?

追跡しなければならないものが少ないほど良い-これが不変オブジェクトの扱いが簡単な理由です。

パブリックフィールドとパブリックゲッター/セッターの両方を持つことを妨げるものは何もありませんが、何を得ることができますか?

場合によっては、1つまたは複数の理由でフィールドに直接アクセスすることが望ましいか、必要である場合がありますが、それが発生した場合は遠慮しないでください。しかし、実際に行うべきなのは、そうすることに明確な利点がある場合のみです。


私の質問のポイントは、同じクラスのフィールドへの直接アクセスについてのみ質問していることです。クライアントコードのゲッターとセッターの目的は既に理解しています。
ダニエルカプラン

@DanielKaplan:私はそれを理解しており、私が言っているのは、実用的な限り、あなたはそれらを同じように扱うべきだということです。
jmoreno

@DanielKaplan:1つのプライベートセッターと1つのパブリックセッターがありますが、少なくとも同じ結果(すべての副作用は同じ)になりますが、発散する可能性以外に何が得られますか?このシナリオとあなたが説明するものとの違いは、ゲッター/セッター以外の状態にアクセスできるようにすることは避けられないが、実際にそうすることは避けることができるということです。必要がない限り、コードを複雑にしないでください。
-jmoreno

1

どちらの方法にもユースケースがあります。パブリックセッターはフィールド値(および/またはバインドされた値)の一貫性を保つため、メソッドロジックがこの一貫性ロジックに干渉しない場合は、セッターを使用する必要があります。「プロパティ」を設定しただけの場合は、setterを使用します。一方、いくつかのフィールドに直接アクセスする必要がある場合があります。たとえば、重いセッターを使用した一括操作や、セッターの概念が単純すぎる操作などです。

一貫性を保つのはあなたの責任です。セッターは定義によりこれを行いますが、複雑なケースをカバーすることはできません。


1

いいえ

ゲッター/セッターが取得および設定するだけの場合(遅延初期化やチェックなしなど)、変数を使用します。将来ゲッター/セッターを変更する場合は、methodWithLogic()クラスを変更しているため非常に適切に変更でき、直接割り当ての代わりにゲッター/セッターを呼び出すことができます。このゲッター/セッターの呼び出しをドキュメント化できます(クラスコードの残りの部分が変数を直接使用しているときにゲッター/セッターを呼び出すのは奇妙なことです)。

JVMは、ゲッター/セッターへの頻繁な呼び出しをインライン化します。そのため、パフォーマンスの問題はありません。変数を使用することの利点は、読みやすさと私見です。

お役に立てれば..

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