別の質問へのフォローアップ(入力ファイルからのモデルデータの読み取りに関する設計上の決定)。
ビルダーまたはファクトリパターンに関して別の質問をしたいと思います。 (ビルダーはファクトリよりも複雑であり、今のところビルダーを使用する必要はないかもしれません)。したがって、私が読まなければならないデータは次のとおりです。
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() {} ... }
メモリを割り当てて移動するのは余分な作業だと思いますが、データの管理に役立ち、余分な入力を続けることができますか?
コメント
回答
データアクセスの問題を解決しようとしています。データアクセスソリューションが必要です。
まず、提案されたソリューションと、それらが問題を解決しない理由を調べてみましょう。
ファクトリパターン
ファクトリデザインパターン(ファクトリメソッドパターンとも呼ばれます)は、多態性を利用したい場合や、オブジェクトの構築を実装クラスから完全に分離したい場合に役立ちます。
クラスベースプログラミングでは、ファクトリメソッドパターンは作成パターンであり、ファクトリメソッドを使用してオブジェクトの作成の問題に対処します。作成されるオブジェクトの正確なクラス。これは、ファクトリメソッド —を呼び出してオブジェクトを作成することによって行われます。コンストラクターを呼び出すのではなく、インターフェイスで子クラスによって実装されるか、基本クラスに実装され、オプションで派生クラスによってオーバーライドされます— 。
(強調、私のもの)
さらに下に詳しく説明しますこのパターンが解決する問題:
ファクトリメソッドデザインパターンは、次のような問題を解決します。
- オブジェクトの作成方法サブクラスがインスタンス化するクラスを再定義できるようにするには?
- クラスがインスタンス化をサブクラスに延期するにはどうすればよいですか?
質問で説明する問題はこれらとは異なります。あなたはサブクラスやポリモーフィズムを扱っていません。ファクトリパターンは解決策ではありません。
ビルダーパターン
ビルダーパターン(ウィキペディアから):
Builderは、オブジェクト指向プログラミングにおけるさまざまなオブジェクト作成の問題に柔軟なソリューションを提供するように設計されたデザインパターンです。 Builderデザインパターンの目的は、複雑なオブジェクトの構築をその表現から分離することです。
(emphasis、mine)
ここでも、説明している問題とこのパターンの使用目的との間に不一致が見られます。
Builderデザインパターンは、次のような問題を解決します。
- クラス(同じ構築プロセス)が複雑なオブジェクト?
- 複雑なオブジェクトを簡略化しますか?
(強調、私の)
ここで重要なのは、オブジェクト(object = 1 object)の構築が複雑であるということです。これは、多数のコンストラクター引数が原因であるか、依存関係のアセンブルが複雑であることが原因である可能性があります。
個々の構造またはクラスは非常に単純であるため、ビルダーパターンも適切ではありません。
データアクセスパターン(解決策)
実際に解決しようとしている問題は、データアクセスの実行方法であり、 データアクセスオブジェクト。
コンピューターソフトウェアでは、データアクセスオブジェクト(DAO)は、ある種のデータベースまたはその他の永続性メカニズムへの抽象的なインターフェースを提供するオブジェクトです。アプリケーション呼び出しを永続性レイヤーにマッピングする、DAO は、データベースの詳細を公開せずに、特定のデータ操作を提供します。
この場合、「データベース」を「テキストファイル」に置き換えます。関連するデザインパターンはリポジトリデザインパターンで、データアクセスを3つの主要なソリューションに分割します。
- リポジトリ—「データアクセス関連」のパブリックインターフェイス
- ゲートウェイ—は、Oracleデータベースとの通信方法または読み取り/書き込み方法を知っています。テキストファイル。これには、オブジェクトと構造体をSQLまたはテキストファイルで期待されるデータ形式にシリアル化することが含まれます。
- ファクトリ—は、ゲートウェイから返されたデータをオブジェクトと構造物にマップします。アプリケーションの他の部分で使用されます
メモリの所有権
メモリの自動管理の利点/制限がないため、割り当てられたメモリの所有権はこれらのオブジェクトと構造は間違いなく懸念事項です。ここで決定を下す必要があります。
- データアクセスオブジェクトはこのメモリのクリーンアップを担当しますか?
- データアクセスオブジェクトはありますか?呼び出し元(クライアントコード)がこのメモリを解放する責任を負うことを期待しますか?
アプリケーションの起動時にデータアクセスオブジェクトが作成され、アプリケーションがシャットダウンされるまで存続する場合、データアクセスオブジェクトがこのメモリの所有権を取得する可能性があります。
データアクセスオブジェクトがオンデマンドで作成されてから破棄される場合、クライアントコードはこのメモリがクリアであることを確認する必要があります
データアクセスオブジェクトのコメントまたはドキュメントでこれを定義し、コードレビューでこれを実施します。
コメント
- 私はこのアプローチが好きです。はるかに柔軟に見えます。私が尋ねる他のことは、その問題についての私の無知を明らかにするでしょう。 DAOを自分で実装する必要がありますか?私がそれを理解する方法で、私はゲートウェイとファクトリーを書くべきですか?ゲートウェイはブリッジGOFパターンと同じですか?
- リポジトリパターンのゲートウェイは、ブリッジパターンの別のバリエーションだと思います。実際、リポジトリパターンの基盤は、特にデータアクセスに焦点を合わせた、より組織化され調整されたブリッジパターンのようです。はい、’これらのコンポーネントを自分で作成する必要があります。
- 投稿したリンクをたどって、en.wikipedia.org/wiki/ODB_(C%2B%2B)、このODBはどのように画像に収まりますか?テキスト入力をsqlliteに変換し、永続化のためにODBを使用するプログラムを作成できますか?この音ですか?私はODBに出くわし、2つの例を見たと言わざるを得ないので、これは情報に基づくコメントではありません。
- このサイトでは、コードの記述に関する支援はトピックから外れていますが、’テキストをsqliteに変換して、既存のライブラリを使用できると確信しています。
回答
ビルダーはファクトリインバリアント(メソッド)内に「ビルド」され、それによってテキストファイルを読み取ります。上記のファイルを読み取るメソッドを使用して1つのファクトリを作成します。これで、読み取ったデータからモデルを段階的にビルドする必要がある場合(ネストされたデータを考えてください)、factoryメソッド内のビルダーパターンが役立ちます。ただし、データをロードするには、おそらく1つのモデルで十分です。
コメント
- 一部の擬似コードは、聞くのに非常に役立ちます。これまでファクトリまたはビルダーパターンを使用したことはありません。
回答
構造のサイズと複雑さを考えると、特にモデルが不変である(または作成できる)場合は、ビルダーが最も適切と思われます。ビルダーとモデルの間の緊密な結合を受け入れる必要がありますが、特に複数の場所でモデルをビルドする必要がある場合は、問題を起こす価値があります。ビルダーを使用すると、検証をカプセル化することもできます。
ただし、モデルが変更可能で、1つの場所でのみ作成される場合は、ファクトリがより適切で便利な場合があり、YAGNIが機能します。
他の質問への回答、生のポインタをC ++に格納しないでください。std::unique_ptr
には欠点がなく、オブジェクトの破壊が簡単になります。そのベクトルがスコープ外になると、メモリが解放されません。記述どおりに参照しますが、unique_ptrを使用するとそうなります。
特に数量が変更される可能性がある場合は、大量の繰り返し要素のベクトルを作成しますが、共有されない中規模のコンポーネントの場合は、それらを構造体に直接含めることをお勧めします。
Model
クラスの派生が異なる場合は”ファクトリ”が適しています(または共通の基本クラスModelBase
またはIModel
)のさまざまな派生であり、どの具象派生をインスタンス化するかについての決定を一元化する必要があります。 ‘質問にこの問題が見当たらないので、ここでファクトリを使用する背後にある’の考えは何ですか?