いつ関数private
を作成する必要があり、なぜそれが良い考えなのですか?
8 に答える
関数private
にアクセスするために他のオブジェクトやクラスが必要ない場合、またはクラス内から関数を呼び出す場合は、関数を作成する必要があります。
最小権限の原則に固執し、絶対に必要な変数/関数へのアクセスのみを許可します。この基準に適合しないものはすべてprivate
.
私は通常、ヘルパー関数を作成しますprivate
。しかし、ヘルパーとは何かが あいまいなようです。では、例を挙げましょう。次のクラスがあるとしますSample
。いくつかのパブリック関数を公開します。そのうちの 1 つは、たとえば isDoWork()
です。この関数は、1 つのパラメーターを取ります。ただし、パラメーターが常に有効であるとは想定していないため、最初に関数の先頭に多くのコードがあるパラメーターの有効性をチェックします。このようなもの:
class Sample
{
public:
void DoWork(SomeClass param)
{
/*
*lots of code related to validation of param
*/
//actual code that operates on the param
//and other member data IF the param is valid
}
};
パラメータの検証に関連する多くのコードを記述したため、関数が煩雑になり、読みにくくなっています。したがって、この検証コードを関数に移動することにし、パラメータを渡してIsValidParam()
この関数を呼び出します。このようなもの:DoWork()
param
class Sample
{
public:
void DoWork(SomeClass param)
{
if ( IsValidParam(param))
{
//actual code that operates on the param
//and other member data IF the param is valid
}
}
};
それはきれいに見えますよね?
さて、あなたはIsValidParam()
クラスのどこかに書きましたが、あなたが今直面している質問は、この関数を作成しますpublic
か? この関数がのような他の関数でのみDoWork()
使用される場合、IsValidParam()
パブリックにすることは意味がありません。それで、あなたはこの関数を作ることにしましたprivate
。
class Sample
{
public:
void DoWork(SomeClass param)
{
if ( IsValidParam(param))
{
//actual code that operates on the param
//and other member data IF the param is valid
}
}
private:
bool IsValidParam(SomeClass param)
{
//check the validity of param.
//return true if valid, else false.
}
};
この種の関数 (IsValidParam) はprivate
. これらの関数をヘルパー関数と呼びます。
この説明がお役に立てば幸いです。
OOPの創設の原則の1つは、カプセル化です。これは、オブジェクトがどのように機能するかに関する機能がそのオブジェクトの内部に保持される場所です。アイデアは、オブジェクトがどのように機能するかを知る必要がない場合、コードがオブジェクトを使用する方が簡単であるということです。電子レンジを購入するようなものです。必要なのは、電子レンジの使い方ではなく、使い方を知っていることだけです。
OOPでも同じアプローチを取る必要があります。オブジェクトをプライベートに維持するために必要なすべてのものを保持します。オブジェクトを完全に使用するために必要なものだけを公開します。
public
クラスを設計している場合 (クライアント コードでクラスを使用する方法を考慮して)、およびおそらくprotected
メンバーで構成されるインターフェイスを必然的に派生させます。
private
メンバーは、それらのパブリック/保護されたメンバーをサポートおよび有効にする関数とデータです。 private
関数は、非メンバーが必要とするコードを因数分解および/またはモジュール化/構造化しprivate
て、実装の冗長性を減らし、理解しやすくする必要があります。
要約するprivate
と、クライアント コードで直接使用するためのものではなく、非private
メンバーをサポートするためだけに存在する場合は、メンバーを作成します。
どのくらい純粋になりたいですか?:)
この質問に対する適切な答えは、不変量の維持に関連しています。これを行う正しい方法はかなり複雑です。
基本クラスでは、クラスへのアクセス全体を提供するパブリックメソッドを定義します。これらの方法はすべて具体的でなければなりません。ここで重要なことは、パブリック不変条件がこれらの関数を呼び出す前後に保持されると想定されていることです。これらの関数は相互に呼び出すことはできません。保護されたプライベートメソッドのみを呼び出します。これらの関数は公理的である必要があります。目的のセマンティクスをキャプチャするために必要な、かなり最小限のセットである必要があります。
これらのメソッドを使用して実行できるほとんどの計算は、グローバルメンバーまたは少なくともパブリック静的メンバーである必要があります。
また、派生クラスの表現に応じて詳細を実装するためのフックである純粋仮想メソッドも提供します。ベースの仮想関数はプライベートである必要があります。ここでの通常のアドバイス(公開)は完全に間違っています。仮想関数は実装の詳細です。1つの例外:仮想デストラクタはパブリックである必要があります。
プライベートヘルパー機能もベースに置くことができます。
ベースに保護されたメソッドがあると便利な場合があります。これらはプライベートヘルパーまたは仮想を呼び出します。上記のように:保護されたメソッドは、保護されたメソッドまたはパブリックメソッドを呼び出さないでください。保護された関数は、各呼び出しの前後でパブリック関数よりも弱い不変条件を維持します。
プライベート関数は通常、非常に弱い不変量を維持します。
この厳密な階層の理由は、オブジェクトの不変条件が正しく維持されるようにするためです。
ここで、派生クラスで実装を提供します。理想的には、ここでの仮想関数は非表示になり、単なるプライベートよりも強力な状態になります。これらの仮想関数は、派生クラスではまったく呼び出さないでください。許可される唯一のアクセスは、基地の保護された機能を介したものです。
デストラクタを含む派生クラスのすべてのメソッドはプライベートである必要があります。コンストラクターはパブリックである必要がありますが、クラスのメソッドではありません。
これらのルールを完全に理解するには、不変条件について慎重に考える必要があります。public invariantsは、publicメソッドを呼び出す前に保持すると見なすことができ、終了後に保持する必要があります。したがって、これらの関数はパブリック関数の開始と終了の間の表現を変更するために使用されるため、クラス内またはクラスから派生したクラスからそのような関数を呼び出すことはできません。したがって、必然的にパブリック不変条件を壊す必要があります。パブリック関数を呼び出さないでください。
同じ議論が保護された関数にも当てはまります。関数は、より弱い不変条件を持つ関数のみを呼び出すことができます。
仮想関数は、パブリック不変条件を壊す操作のシーケンスが、壊れた不変条件でパブリックに戻ることによって中断されないようにするために、常に基本クラスのパブリックラッパーからパブリックによって呼び出されます。仮想のセット自体は、表現が持つ必要のある不変構造を表します。これを行うことにより、パブリッククライアントの計算を実行するための表現のすべての操作をベースに抽象化できます。
実際には、これらのルールは通常、些細なラッパーを生成することが多く、作成および保守するための余分なコードが多いため、通常は従いません。そのため、原則としてこれが完全かつ完全に間違っている場合でも、仮想関数は公開されることがよくあります。
関数またはクラスを作成する前に、その関数またはクラスのスコープがグローバルかローカルかを理解する必要があります。
例: "ConnectionString()"。すべてのデータベース接続には「ConnectionString()」が必要なので、宣言されたPublicです。
private:このクラスでのみ使用され、他のクラスや派生クラスでは使用されません。
保護:このクラスおよびおそらく派生クラスによって使用されますが、他のクラスによっては使用されません。
public:他のクラス、このクラス、および派生クラスによって使用されます。
プライベートと保護のどちらかを選択するのは難しいです。したがって、派生クラスが関数を必要とする可能性が1%ある場合は、常に関数を保護します。
ここで反論を投げます: 関数を にする代わりに、private
多くの場合、 または のいずれかにする必要がありprotected
ますvirtual
。API の呼び出しまたは使用には問題がない (つまりpublic
関数に問題はない) が、クラスの実装が不十分であるか、拡張する必要がある場合を考えてみましょう。(もちろん) 基本クラスを派生させ、public
変更する必要がある公開された関数に新しい定義を提供します。
最初の問題は、派生クラスが改善を実装するための内部変数またはヘルパー関数に十分にアクセスできない場合です。つまり、公開された api 関数を書き直すには、ヘルパー関数なしでは不可能です。上記のNawazの優れた回答を借りるには、外部で定義されたクラスをパラメーターSomeClass
としてpublic
-ly公開されたapivoid DoWork(SomeClass param)
に渡し、ヘルパー関数で検証するbool IsValidParam(SomeClass param)
ため、書き直す必要がある場合DoWork
、既存のヘルパー関数がないと面倒ですとしてIsValidParam
のみ実行できますprotected
。
2 つ目の問題は、公開されている呼び出し関数全体を再実装するのではなく、内部関数を上書きする必要がある場合です。この場合、内部ヘルパー関数を宣言できますvirtual
。Private Virtual を使用する場合も参照してください。上記の例のようにSomeClass
、または派生物を変更した場合、または別のコンテキストで別の (厳密な? 緩い?) 検証が必要な場合、オーバーライドするだけで、関数virtual IsValidParam
をそのままにしておくことができます。DoWork
一般に、クラスを継承するか、それをベースとして使用する可能性がある場合は、とにかくそれを関数にする方がよいでしょうvirtual
。
ソフトウェアの性質やアーキテクチャの設計によっては、初期段階ですべての関数を宣言しpublic virtual
たりprotected virtual
、公開された API を制限したりすると便利な場合があります。最後に、必要に応じて を引き出すvirtual
か宣言しprivate
ます。ただし、制限はありませんが、下流の改善が容易になります。特に、すでに「修正済み」のライブラリや変更が難しいライブラリを編集する必要が減る場合。