別の質問のフォローアップ( 入力ファイルからのモデルデータの読み取りに関する設計上の決定 )。
ビルダーやファクトリーパターンについてもう一度質問したいと思います。 (ビルダーはファクトリーよりも複雑であり、今のところビルダーを使用する必要がない可能性があることを読みました)。だからここに私が読まなければならないデータがあります:
TABLE: "DEGREES OF FREEDOM"
X=Yes Y=Yes Z=Yes R1=Yes R2=Yes R3=Yes
TABLE: "ANALYSIS OPTIONS"
Solver=Advanced SolverProc=Advanced
TABLE: "COORDINATE SYSTEMS"
Name=GLOBAL Type=Cartesian X=0 Y=0 Z=0
TABLE: "GRID LINES"
CoordSys=GLOBAL AxisDir=X GridID=A XRYZCoord=-720 LineColor=Gray8Dark Visible=Yes
CoordSys=GLOBAL AxisDir=X GridID=B XRYZCoord=-432 LineColor=Gray8Dark Visible=Yes
CoordSys=GLOBAL AxisDir=X GridID=C XRYZCoord=-144 LineColor=Gray8Dark Visible=Yes
CoordSys=GLOBAL AxisDir=X GridID=D XRYZCoord=144 LineColor=Gray8Dark Visible=Yes
CoordSys=GLOBAL AxisDir=X GridID=E XRYZCoord=432 LineColor=Gray8Dark Visible=Yes
CoordSys=GLOBAL AxisDir=X GridID=F XRYZCoord=720 LineColor=Gray8Dark Visible=Yes
...
このようなテーブルは他にもたくさんあります。一部のテーブルには親子関係があります(各座標系には独自のグリッド線があります)。次のように、各テーブルを表す構造を作成しました。
struct ActiveDOF
{
bool Ux;
bool Uy;
bool Uz;
bool Rx;
bool Ry;
bool Rz;
};
struct GridLine
{
enum Direction{X, Y, Z};
enum Type{PRIMARY, SECONDARY};
enum Location{START, END};
std::string coodSystem;
Direction direction;
std::string gridID;
float XRYZ;
Type lineType;
QColor color;
bool visible;
Location bubleLoc;
bool allVisible;
float bubleSize;
};
struct CoordinateSystem
{
std::string name;
std::string type;
QVector3D center; // center x, center y, cetner z
QVector3D about; // aboutx, about y, about z
std::vector<GridLine> gridLines;
};
これらのデータ構造はモデルクラスに組み込まれ、次のような50の奇数データ構造があるため、巨大なクラスになります。
class Model
{
private:
ActiveDOF activeDOF;
CoordinateSystem coordinateSystem;
....
public:
Model() {} ...
}
各テーブルには、setメソッドとgetメソッドが必要です。この設計を変更すると、非常に時間がかかるので心配です。私はどんな提案にも感謝します。ここでの情報も、以前の質問をよりよく理解できると思います。
現在、状況を考慮して、ビルダーまたはファクトリーメソッドをどこに置くべきかわかりません。コードとUMLチャートをいくつか見ましたが、ファクトリーまたはビルダーを実装して設計に必要な構造を作成する方法を理解できませんでした。モデル内で変更される可能性があるため、各テーブルに名前でアクセスする必要があります。当面は、仮想ベースクラスのサブクラスにすることを避けて、それらをコンテナ。
また、データ構造体のインスタンスを宣言する代わりに、それらへのポインタを保持する必要があるのは理にかなっていますか?すべてのデータ構造がRecordという仮想基本クラスから派生している場合、モデルは次のようになります。
class Model
{
private:
ActiveDOF* activeDOF;
CoordinateSystem* coordinateSystem;
....
std::Vector<Record*> data;
public:
Model() {} ...
}
メモリを割り当てたり移動したりするのは余分な作業だと思いますが、データを管理して余分なタイピングを続けるのに役立ちますか?私はそれを仮定するのは正しいですか?
データアクセスの問題を解決しようとしていて、データアクセスソリューションが必要です。
最初に、提案されたソリューションと、なぜそれらがあなたの問題を解決しないのかを調べてみましょう。
ファクトリデザインパターン(ファクトリメソッドパターンとも呼ばれる)は、ポリモーフィズムを利用したい場合や、オブジェクトの構築を実装クラスから完全に分離したい場合に役立ちます。
ウィキペディア :
クラスベースのプログラミングでは、ファクトリメソッドパターンは、ファクトリメソッドを使用して正確に指定せずにオブジェクトを作成する問題に対処する作成パターンです作成されるオブジェクトのクラス。これは、ファクトリメソッドを呼び出してオブジェクトを作成することによって行われます(インターフェイスで指定され、子クラスで実装されるか、基本クラスで実装され、オプションでオーバーライドされます)。コンストラクターを呼び出すのではなく、派生クラスによって
(強調、私の)
さらに下に、このパターンが解決する問題について詳しく説明します。
Factory Method設計パターンは、次のような問題を解決します。
- sub classesがインスタンス化するクラスを再定義できるように、オブジェクトをどのように作成できますか?
- クラスはどのようにインスタンス化をサブクラスに遅らせることができますか?
あなたが質問で説明する問題はこれらとは異なります。あなたはサブクラスやポリモーフィズムを扱っていません。ファクトリーパターンは解決策ではありません。
ビルダーパターン( Wikipedia から):
Builderは、オブジェクト指向プログラミングにおけるさまざまなオブジェクト作成の問題に対する柔軟なソリューションを提供するように設計された設計パターンです。 Builderデザインパターンの目的は、複雑なオブジェクトの構築とその表現を分離することです。
(強調、私の)
ここでも、説明している問題とこのパターンの使用目的の間に不一致が見られます。
Builder設計パターンは、次のような問題を解決します。
- クラス(同じ構築プロセス)が複雑なオブジェクトの異なる表現を作成するにはどうすればよいですか?
- 複雑なオブジェクトの作成を含むクラスをどのように簡略化できますか?
(強調、私の)
ここで重要なのは、オブジェクト(オブジェクト= 1オブジェクト)の構造が複雑であることです。これは、コンストラクター引数の数が多いか、依存関係の組み立てが複雑なことが原因である可能性があります。
個々に、各構造体またはクラスはかなり単純なので、ビルダーパターンも適切ではありません。
あなたが実際に解決しようとしている問題はデータアクセスを実行する方法であり、あなたは データアクセスオブジェクト を探しているかもしれません。
コンピュータソフトウェアでは、データアクセスオブジェクト(DAO)は、あるタイプのデータベースまたは他の永続化メカニズムへの抽象インターフェースを提供するオブジェクトです。アプリケーションをマッピングするDAOは永続化レイヤーへの呼び出しで、データベースの詳細を公開せずに特定のデータ操作を提供します。
あなたの場合、「データベース」を「テキストファイル」に置き換えてください。関連する設計パターンは Repository Design Pattern で、データアクセスを3つの主要なソリューションに分割します。
メモリの自動管理の利点/制限がないため、これらのオブジェクトと構造体に割り当てられたメモリの所有権は間違いなく懸念事項です。ここであなたは決定をしなければなりません:
アプリケーションの起動時にデータアクセスオブジェクトが作成され、アプリケーションがシャットダウンされるまで存続する場合、データアクセスオブジェクトがこのメモリの所有権を取得する可能性があります。
データアクセスオブジェクトがオンデマンドで作成されて破棄される場合、クライアントコードはこのメモリがクリーンアップされていることを確認する必要があります。
データアクセスオブジェクトのコメントまたはドキュメントでこれを定義し、コードレビューでこれを実施します。
ビルダーはファクトリインバリアント(メソッド)内に「ビルド」され、それによってテキストファイルを読み取るには、1つのファクトリに、そのファイルを読み取るメソッドを記述します。これで、読み取ったデータからモデルを段階的に構築する必要がある場合(ネストされたデータと考えてください)、ファクトリーメソッド内のビルダーパターンが役立ちます。ただし、データをロードするには、おそらく単一のモデルで十分です。
構造のサイズと複雑さを考えると、特にモデルが不変である(または作成できる)場合は、ビルダーが最も適切と思われます。ビルダーとモデル間の緊密な結合を受け入れる必要がありますが、特に複数の場所でモデルを構築する必要がある場合は、問題に値します。ビルダーを使用すると、検証をカプセル化することもできます。
ただし、モデルが変更可能であり、1か所でしか作成されない場合は、ファクトリーがより適切で便利な場合があり、YAGNIが役立ちます。
他の質問への回答として、C++で未加工のポインタを保存しないでください。事実はstd::unique_ptr
には欠点がなく、オブジェクトの破壊を簡単にします。そのベクトルが範囲外になると、それが参照するメモリが解放されたときに解放されませんが、unique_ptrを使用すると解放されます。
特に量が変化する可能性がある場合は、大量の繰り返し要素のベクトルを作成しますが、共有されない中規模のコンポーネントの場合は、構造体に直接含めることもできます。