データメンバまたはメソッドにクラス定数を保存する方が良いでしょうか?
質問
最近、Bスプライン曲線をレンダリングするクラスを作成しました。これらの曲線は、多数の制御点によって定義されます。もともと、8つのコントロールポイントを使用するつもりだったので、次のようにクラスに定数を追加しました。
class Curve
{
public:
static const int CONTROL_POINT_COUNT = 8;
};
次に、このクラスを拡張して、任意の量のコントロールポイントを許可します。これを次のように変更します。
class Curve
{
public:
int getControlPointCount() {return _controlPointCount;}
};
問題は、適応性を促進するために、最初にメソッドに定数を保存する方が良いかどうかです。言い換えれば、こうして始めた方が良いのではないでしょうか:
class Curve
{
public:
int getControlPointCount() {return 8;}
};
これの利点は、定数などを移動するのではなく、問題のメソッドの1つのシンボルを変更しただけだったということです。
これは良い習慣ですか、それとも悪い習慣ですか?
解決
通常、手動で可能な限り少ないカップリングを維持することを好みます。
曲線内の制御点の数は、曲線内の制御点の数です。自由に設定できる独立変数ではありません。
そのため、通常はconst標準コンテナ参照を公開します:
class Curve
{
private:
std::vector<Point>& _controlPoints;
public:
Curve ( const std::vector<Point>& controlPoints) : _controlPoints(controlPoints)
{
}
const std::vector<Point>& getControlPoints ()
{
return _controlPoints;
}
};
コントロールポイントの数を知りたい場合は、 curve.getControlPoints()。size()
を使用します。とにかくポイントとカウントを必要とするほとんどのユースケースでは、標準コンテナを公開することで、カウントを取得して呼び出すのではなく、標準ライブラリのイテレータイディオムと組み込みアルゴリズムを使用できると思われますループ内の getControlPointWithIndex
などの関数。
曲線クラスに実際に他に何もない場合は、次のことまで行ってください:
typedef std::vector<Point> Curve;
(多くの場合、レンダラークラスはレンダリングパイプラインの詳細を保持できるため、曲線自体はレンダリングされず、曲線は純粋に幾何学的なアーティファクトのままになります)
他のヒント
int getControlPointCount() {return _controlPointCount;}
これはアクセサーです。 litbが指摘しているように、アクセサーのconst staticをスワップすることは、実際にはゲインではありません。あなたが本当に必要とするものは、将来を保証するために、おそらくアクセサとミューテータのペアです。
int getControlPointCount() {return _controlPointCount;} // accessor
また、アクセサーの設計定数を投入して作成します:
int getControlPointCount() const {return _controlPointCount;} // accessor
および対応する:
void setControlPointCount(int cpc) { _controlPointCount = cpc;} //mutator
現在、静的オブジェクトとの大きな違いは、コントロールポイントカウントがクラスレベルの属性ではなく、インスタンスレベルの属性になっていることです。これは設計変更です。このようにしたいですか?
Nit:クラスレベルの静的カウントは public
であるため、アクセサーは不要です。
質問に適切に回答するには、controlPointCount変数の設定方法も知っておく必要があります。クラスの外に設置されていますか?この場合、セッターも定義する必要があります。それとも、Curveクラスがそれを設定する唯一の責任ですか?コンパイル時または実行時にのみ設定されますか?
とにかく、この形式でもマジックナンバーは避けてください:
int getControlPointCount() {return 8;}
これは優れています:
int getControlPointCount() {return CONTROL_POINT_COUNT;}
メソッドには、クラスの外部に影響を与えることなく、内部実装を変更できる(定数値を使用し、構成ファイルから読み取り、値を動的に変更できる)利点があります。
class Curve
{
private:
int _controlPointCount;
void setControlPointCount(int cpc_arg)
{
_controlPointCount = cpc_arg;
}
public:
curve()
{
_controlPointCount = 8;
}
int getControlPointCount() const
{
return _controlPointCount;
}
};
次の開発フェーズに移るまで、コントロールポイントカウントで遊ぶことができないように、set関数をプライベートにしてこのようなコードを作成します。ランタイム。その時点で、このsetメソッドをプライベートスコープからパブリックスコープに移動できます。
質問を理解している間、例にはいくつかの概念的な問題があります:
- 制御点の数に制限がない場合の
getControlPointCount()
の戻り値は何ですか?- MAXINTですか?
- それは曲線上の制御点の現在の数ですか?
- 実際にMAXINTポイントで曲線を作成しようとするとどうなりますか?最終的にはメモリ不足になります。
インターフェース自体に問題があるようです。他の標準コレクションクラスと同様に、クラスはポイント数の制限をカプセル化する必要があり、サイズ、メモリ、またはその他の違反が発生した場合、その AddControlPoint()
はエラーを返します。
具体的な答えについては、kgiannakakisに同意します。メンバー関数を使用すると柔軟性が高まります。
プログラムの実行を通じて、すべての「安定した」値に構成+定数(デフォルト値)を使用する傾向があります。変更できない値(360度-&gt; 2 piラジアン、60秒-&gt; 1分)または変更により実行中のコードが破損する(それらを不安定にするアルゴリズムの最小値/最大値)値の単純な定数を使用します。
あなたはいくつかの異なる設計上の問題を扱っています。最初に、制御点の数がクラスまたはインスタンスレベルの値であるかどうかを知る必要があります。次に、2つのレベルのいずれかで定数であるかどうか。
すべての曲線がアプリケーションで同じ数の制御点を共有する必要がある場合、それはクラスレベル(静的)値です。異なる曲線が異なる数の制御点を持つことができる場合、それはクラスレベルの値ではなく、インスタンスレベルの値です。
この場合、コントロールポイントの数が曲線の寿命全体で一定である場合、インスタンスレベルの定数であり、変更できる場合、このレベルでも一定ではありません。
// Assuming that different curves can have different
// number of control points, but that the value cannot
// change dynamically for a curve.
class Curve
{
public:
explicit Curve( int control_points )
: control_points_( control_points )
{}
// ...
private:
const int control_points_;
};
namespace constant
{
const int spline_control_points = 8;
}
class Config
{
public:
Config();
void readFile( std::string const & file );
// returns the configured value for SplineControlPoints or
// constant::spline_control_points if the option does not
// appear in config.
int getSplineControlPoints() const;
};
int main()
{
Config config;
config.readFile( "~/.configuration" ); // read config
Curve c( config.getSplineControlPoints() );
}
整数型の場合、私は通常次を使用しています:
class Curve
{
public:
enum
{
CONTROL_POINT_COUNT = 8
};
};
クラス実装以外のエンティティに定数が必要ない場合は、*。cppファイルで定数を宣言します。
namespace
{
const int CONTROL_POINT_COUNT = 8;
}
一般に、すべてのデータはプライベートであり、ゲッターとセッターを介してアクセスする必要があります。そうしないと、カプセル化に違反します。つまり、基になるデータを公開すると、自分自身とクラスをその基になるデータの特定の表現にロックします。
この特定のケースでは、次のようなことをしたと思います。
class Curve
{
protected:
int getControlPointCount() {return _controlPointCount;}
int setControlPointCount(int c) { _controlPointCount = c; }
private:
static int _controlPointCount = 0;
};
定数は一般にメソッド内で定義しないでください。選択する例には、2つの独自の機能があります。まず、ゲッターです。次に、返される型はintです。しかし、定数を定義するポイントは、定数を複数回使用し、それらを便利な方法で参照できるようにすることです。 「8」を入力&quot; controlPointCount&quot;とは対照的に時間を節約でき、メンテナンスコストが発生しないように見えるかもしれませんが、メソッド内で常に定数を定義している場合、これは通常当てはまりません。