5

私は現在、C ++/Qtアプリケーションをシリアル化するための優れたオブジェクト指向設計を探しています。
次の図のように、Composite-Patternで実装されたツリー構造に基づいて編成されたアプリケーションのクラスを想像してみてください。

私が考えた2つの可能な原則:

1。)
シリアル化可能でなければならないすべてのクラスにsave()/ load()関数を配置します。これを何度も見た場合、通常はブーストで実装されます。クラスのどこかに、次のようなものがあります。

friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, const unsigned int version)
{
    ar & m_meber1;
}

これをsave()とload()に分けることもできます。ただし、このアプローチの欠点は次のとおり
です。2か月後にシリアル化を変更したくない場合(XML、HTML、またはサポートされていない非常に興味深いものに)、数千のクラスすべてを採用する必要があります。私の意見では、これは良いオブジェクト指向デザインではありません。
また、さまざまなシリアル化(XML、バイナリ、ASCIIなど)を同時にサポートしたい場合は、シリアル化機能のためだけにcppの80%が存在します。

2.)
Boostは、非侵入型のシリアル化

「http://www.boost.org/doc/libs/1_49_0/libs/serialization/doc/tutorial.html」も提供していることを知っています。

したがって、別の方法は、複合ツリー構造を反復処理し、すべてのオブジェクトをシリアル化するイテレータ(および逆シリアル化用の1つのイテレータ)を実装することです
(これは、.NET-FrameworkのXmlSerializer-Classが行うことだと思いますが、i ^ m .NETに本当に精通している)
これは、save()とload()を分離し、シリアル化が変更された場合に変更できる場所が1つしかないため、より適切に聞こえます。
したがって、これはより良いように聞こえますが、
次のようになります。-シリアル化する必要のあるすべてのパラメーターに対して、setter()とgetter()を提供する必要があります。(つまり、プライベートデータはもうありません。(これは良い/悪いですか?))
-複合ツリーに長い継承のhirarchy(5クラス以上)がぶら下がっている可能性があります。
では、派生クラスのsetter()/ getter()をどのように呼び出すのでしょうか。ベースのComposite-Componentのインターフェース関数のみを呼び出すことができる場合。

もう1つの方法は、オブジェクトデータを個別の抽象形式にシリアル化することです。そこから、可能なすべての後続のシリアル化(XML、TEXT、可能なものは何でも)がデータを取得します。1つのアイデアは、それをQDomNodeにシリアル化することでした。しかし、余分な抽象化はパフォーマンスを犠牲にするだろうと思います。

だから私の質問は:
シリアル化のための良いオブジェクト指向デザインを知っている人はいますか?
たぶん、JAVA、Python、C#などの他のプログラミング言語から。

ありがとうございました。

4

1 に答える 1

4

シリアル化に注意してください。

シリアル化とは、メモリ内表現のスナップショットを取り、後で復元することです。

これはすべて素晴らしいことですが、以前に保存したスナップショットを新しいバージョンのソフトウェア (後方互換性)でロードすることを考えると、継ぎ目でほつれ始めるか、(神は禁じられています) 最近保存したスナップショットを古いバージョンのソフトウェア (前方互換性)。

多くの構造は下位互換性に簡単に対処できますが、上位互換性を維持するには、新しい形式が以前の反復に非常に近い必要があります。基本的には、いくつかのフィールドを追加/削除するだけで、全体的な構造は同じままです。

問題は、シリアライゼーションがパフォーマンス上の理由から、ディスク上の構造をメモリ内の表現に結びつける傾向があることです。メモリ内表現を変更するには、古いアーカイブの非推奨 (および/または移行ユーティリティ) が必要です。

一方、メッセージングシステム (これが google protobuf です) は、交換されたメッセージ構造をメモリ内表現から切り離して、アプリケーションの柔軟性を維持することを目的としています。

したがって、最初にシリアライゼーションメッセージングのどちらを実装するかを選択する必要があります。


これで、保存/読み込みコードをクラス内またはクラス外に記述できることがわかりました。これもトレードオフです。

  • クラス内コードはすべてのメンバーにすぐにアクセスでき、通常はより効率的で簡単ですが、柔軟性が低いため、シリアライゼーションと密接に関連しています。
  • クラス外のコードは間接的なアクセス (ゲッター、ビジター階層) を必要とし、効率は劣りますが柔軟性が高いため、メッセージングと連携します。

hidden stateについての欠点がないことに注意してください。Aclassには (真の) 非表示状態はありません。

  • キャッシュ (mutable値) はまさにそれであり、心配なく失われる可能性があります
  • 隠しタイプ (考えるFILE*または他のハンドル) は、通常、他の方法 (たとえば、ファイルの名前をシリアル化する) によって回復可能です。
  • ...

個人的には両方を混ぜて使っています。

  • キャッシュは、現在のバージョンのプログラム用に書き込まれ、.NET で高速 (非) シリアル化を使用しv1ます。新しいコードは と の両方で動作するように記述されてv1おり、以前のバージョンがなくなるまでデフォルトv2で書き込みます。v1次に、書き込みに切り替えますv2(簡単だと仮定します)。場合によっては、大規模なリファクタリングによって下位互換性が非常に苦痛になることがあります。この時点で、それを破棄します (そして、主要な桁を増やします)。
  • 一方、他のアプリケーション/サービスとのやり取りや、より耐久性のあるストレージ (データベースまたはファイル内の BLOB) ではメッセージングを使用します。これは、今後 10 年間、特定のコード構造に縛られたくないからです。

注: 私はサーバー アプリケーションに取り組んでいるので、私のアドバイスはそのような環境の詳細を反映しています。クライアント側のアプリは古いバージョンを永久にサポートする必要があると思います...

于 2012-06-05T19:54:59.483 に答える