SOでC++のアクセサメソッドに関するいくつかの質問が出されましたが、この問題に対する私の好奇心を満たすものはありませんでした。
Stroustrupや他の有名なプログラマのように、それらの多くを含むクラスは悪いオブジェクト指向の兆候だと思うので、可能な限りアクセッサを避けるようにします。 C++では、ほとんどの場合、クラスに責任を追加したり、friendキーワードを使用してそれらを回避したりできます。しかし、場合によっては、特定のクラスメンバーへのアクセスが本当に必要です。
いくつかの可能性があります。
1。アクセサーをまったく使用しない
それぞれのメンバー変数を公開するだけです。これはJavaでは禁止されていますが、C++コミュニティでは問題ないようです。ただし、明示的なコピーまたはオブジェクトへの読み取り専用(定数)参照が返される必要がある場合は少し心配ですが、それは誇張されていますか?
2。Javaスタイルのget/setメソッドを使用
Javaからのものかどうかはわかりませんが、私はこれを意味します:
int getAmount(); // Returns the amount
void setAmount(int amount); // Sets the amount
。Objective Cスタイルのget/setメソッドを使用
これは少し奇妙ですが、明らかに一般的になっています。
int amount(); // Returns the amount
void amount(int amount); // Sets the amount
それが機能するためには、メンバー変数の別の名前を見つける必要があります。アンダースコアを追加する人もいれば、「m_」を追加する人もいます。私も好きではありません。
どのスタイルを使用しますか?その理由は?
保守の観点から、400万行のC++コード(それは1つのプロジェクトに過ぎません)に座っているという私の観点から、私は言うでしょう:
メンバーが不変(つまり、const
)または依存関係のない単純なメンバー(メンバーXおよびYのポイントクラスなど)の場合、ゲッター/セッターを使用しないでかまいません。
メンバーがprivate
のみの場合、ゲッター/セッターをスキップしてもかまいません。 .cppユニットが小さければ、内部 pimpl -classesのメンバーもprivate
としてカウントします。
メンバーがpublic
またはprotected
(protected
はpublic
)およびnon _const
、non-simple、または依存関係がある場合は、ゲッター/セッターを使用します。
メンテナンス担当者として、ゲッター/セッターを持ちたい私の主な理由は、ブレークポイント/ロギング/何かを置く場所があるからです。
選択肢2のスタイルの方が検索性が高いため(メンテナンス可能なコードを作成する際の重要なコンポーネント)、私は好みです。
2)それはあなたの意図を最も明確にするので、最高のIMOです。 set_amount(10)
はamount(10)
よりも意味があり、ニースの副作用として、amount
という名前のメンバーが許可されます。
パブリック変数は通常のアイデアではありません。カプセル化がないためです。変数が更新されたときにキャッシュを更新するか、ウィンドウを更新する必要があるとしますか?変数がパブリックの場合は残念です。 setメソッドがある場合は、そこに追加できます。
私はこのスタイルを使用しません。クラス設計の将来を制限する可能性があるため、優れたコンパイラーを使用すれば明示的なゲッターまたはセッターも同様に効率的です。
もちろん、実際には、インラインの明示的なゲッターまたはセッターは、クラス実装に対する基礎となる依存関係を作成します。意味の依存関係を減らすだけです。変更した場合は、すべてを再コンパイルする必要があります。
これは、アクセサメソッドを使用するときのデフォルトのスタイルです。
このスタイルは私にはあまりにも「賢い」ようです。私はまれにそれを使用しますが、アクセサに可能な限り変数のように感じさせたい場合のみです。
おそらくコンストラクターを使用して単純な変数のバッグを作成し、それらがすべて正常な状態に初期化されるようにする場合があると思います。これを行うときは、単にstruct
にして、すべて公開します。
pure
データを表現したいだけなら、これは良いスタイルです。
私はそれが好きではありません:) get_/set_
はC++でオーバーロードすることができる場合、本当に不要です。
STLは、避けるべき場合を除いて、std::streamString::str
やstd::ios_base::flags
などのこのスタイルを使用します!いつ?メソッドの名前が他の型の名前と競合する場合、get_/set_
が原因でstd::string::get_allocator
などのstd::allocator
スタイルが使用されます。
一般的に、システム内のエンティティが多すぎるため、ゲッターとセッターが多すぎるのは良い考えではないと思います。これは、設計の誤りまたはカプセル化の誤りを示しています。
そうは言っても、そのようなデザインをリファクタリングする必要があり、ソースコードが利用可能な場合は、Visitor Designパターンを使用することをお勧めします。その理由は:
a。クラスにプライベート状態へのアクセスを許可する人を決定する機会を与えます
b。クラスに、プライベート状態に関心のある各エンティティに許可するアクセス権を決定する機会を与えます
c。明確なクラスインターフェイスを介して、このような外部アクセスを明確に文書化します。
基本的な考え方は次のとおりです。
a)可能な場合は再設計します。
b)次のようなリファクタリング
クラス状態へのすべてのアクセスは、よく知られた個別インターフェースを介して行われます
そのような各インターフェースに対して、ある種のやるべきことを設定することが可能であるべきです。外部エンティティからのすべてのアクセス[〜#〜] good [〜#〜]は許可する必要があり、外部エンティティからのすべてのアクセス[〜#〜] bad [〜#〜]禁止する必要があり、外部エンティティ[〜#〜] ok [〜#〜]の取得は許可するが設定は許可しない(たとえば)
アクセサーを使用から除外しません。一部のPOD構造については可能性がありますが、私はそれらを良いことだと考えています(一部のアクセサーには追加のロジックもあるかもしれません)。
コードに一貫性がある場合、命名規則はあまり重要ではありません。いくつかのサードパーティライブラリを使用している場合、いずれにしても異なる命名規則を使用する可能性があります。だからそれは好みの問題です。
意味のあるデータを参照するために、整数型ではなくクラスの理想化を見てきました。
以下のようなものは通常、C++プロパティをうまく利用していません。
struct particle {
float mass;
float acceleration;
float velocity;
} p;
どうして? p.mass * p.accelerationの結果はフロートであり、期待どおりに強制されないためです。
目的を指定するクラスの定義(前述のamountなどの値であっても)はより意味があり、次のようなことができます。
struct amount
{
int value;
amount() : value( 0 ) {}
amount( int value0 ) : value( value0 ) {}
operator int()& { return value; }
operator int()const& { return value; }
amount& operator = ( int const newvalue )
{
value = newvalue;
return *this;
}
};
演算子intによって暗黙的に量の値にアクセスできます。さらに:
struct wage
{
amount balance;
operator amount()& { return balance; }
operator amount()const& { return balance; }
wage& operator = ( amount const& newbalance )
{
balance = newbalance;
return *this;
}
};
ゲッター/セッターの使用法:
void wage_test()
{
wage worker;
(amount&)worker = 100; // if you like this, can remove = operator
worker = amount(105); // an alternative if the first one is too weird
int value = (amount)worker; // getting amount is more clear
}
これは異なるアプローチであり、良いことも悪いことも意味しませんが、異なることを意味します。
追加の可能性は次のとおりです。
int& amount();
推奨するかどうかはわかりませんが、通常とは異なる表記法によりユーザーがデータを変更するのを控えることができるという利点があります。
str.length() = 5; // Ok string is a very bad example :)
時にはそれが良い選択かもしれません:
image(point) = 255;
もう1つの可能性として、関数表記を使用してオブジェクトを変更します。
edit::change_amount(obj, val)
このように、危険な/編集機能は、独自のドキュメントを使用して別のネームスペースに移動できます。これは一般的なプログラミングに自然に付属しているようです。
もう1つの可能性についてお話ししましょう。これは最も簡潔なようです。
その変数をpublicと宣言するだけです:
class Worker {
public:
int wage = 5000;
}
worker.wage = 8000;
cout << worker.wage << endl;
class Worker {
int _wage = 5000;
public:
inline int wage() {
return _wage;
}
}
worker.wage = 8000; // error !!
cout << worker.wage() << endl;
このアプローチの欠点は、アクセスパターンを変更するときに、すべての呼び出しコードを変更する(つまり、括弧を追加する)必要があることです。