あなたの問題について私が知っていることに基づいて、最初のアプローチは合理的だと思います。2 番目のアプローチは、マッピングが常に単純であり、多くのマッピング変更を行うという確信がない限り、必要以上に複雑に見えます。
ただし、より完全な回答を得るために、私があなたの立場で検討する 3 つのアプローチについて説明します。各アプローチは結合と複雑さによって異なります。そのうちの 2 つについては基本的に説明しましたが、もう少し詳しく説明します。問題の全容がわからないため、それらのいずれも「信頼できる解決策」とは見なしませんが、言及する価値があると思います。
私が検討する最高の結合で最も複雑でないアプローチDocument
は、 、Track
、およびの静的ファクトリ関数のセットですClip
。これは、本質的にあなたが言及した最初のオプションです。このアプローチがあまり使用されているのを見たことがありませんが、他の開発者が使用している可能性があります。私にはRuby/ActiveRecord
感じがあります(これは判断ではなく、ただのランダムな考えです)。
//all examples are C++ish pseudo-code
Document* Document::fromXML(SomeXMLStream* stream) {
Document* doc = new Document();
//read the details specific to Document, populate *doc
//for each <Track> child in the stream...
Track* track = Track::fromXML(stream);
//add the track to *doc
return doc;
}
Track* Track::fromXML(SomeXMLStream* stream) {
Track* track = new Track();
//similar steps here
//for each <Clip> child in the stream...
Clip* clip = Clip::fromXML(stream);
//and so on
return track;
}
//similar code for Clip::fromXML(...)
結合度が高い (つまり、クラスが XML を認識している)とfromXML
、ロジックをロジックのすぐ隣に配置できるという利点があります。toXML
これは、ライターとリーダーを同じ場所で定義するのが合理的 (かつ便利) であるためです。XML レイアウトの変更には 2 つの変更が必要ですが (1 つはfromXML
で、もう 1 つは でtoXML
)、変更は 1 つのファイルで行われます。
このアプローチのマイナス面はtoXML
、クラス自体にコーディングする場合と同じマイナス面です。XML はハードコーディングされるため、そのままの XML を使用したほうがよいでしょう。toXML
しかし、実装に専念しているのであれば、 fromXML
.
私が検討する2 番目のアプローチは、デシリアライザー (またはマッパー、マーシャラーなど、お好きな呼び方) を導入して、XML のアービターとして機能させることです。XML とモデルの結合は、 、 、および からこれらのデシリアライザーに移動Document
しTrack
ますClip
。このアプローチは、手書きコードと自動生成コードの両方で「現場」で頻繁に使用されるのを見てきました。
Document* DocumentDeserializer::fromXML(SomeXMLStream* stream) {
Document *doc = new Document();
//read the details specific to Document, populate *doc
//for each <Track> child in the stream...
Track* track = TrackDeserializer::fromXML(stream);
//add the track to *doc
return doc;
}
//similar code for Track and Clip
このアプローチの明らかな欠点は、デシリアライザーを使用してクラスで XML を記述しているのにtoXML
、デシリアライザーを使用してそれを読み取っていることです。したがって、XML レイアウトを変更すると、2 つのクラスが変更されることになります。が同じクラスに移動した場合toXML
(おそらくクラスを呼び出します<ModelClassName>XMLMapper
)、この欠点は解消されます。
このアプローチの小さな欠点の 1 つは、変更のたびにファイルのペア (モデル クラスとデシリアライザー クラス) を変更する必要があるため、モデル クラスと XML の同期が少し複雑になることです。これは、モデル クラスから XML コードを取得するためだけに価値がある場合があります。
このアプローチから得られるデカップリングにより、モデル クラスが簡素化され、たとえば XML 以外のものを使用してオブジェクトを格納および送信するなど、将来の入力と出力の柔軟性が向上します。また、XML 固有のコードを独自のファイル セットに封鎖します。
カップリングが最も低く、最も複雑なアプローチは、前述のデシリアライザー/マッパーのアプローチに似ていますが、マッピングの詳細がより宣言的な方法で抽象化されています-2番目のアプローチと同様です。luabind
このアプローチが、および他の「C++ からスクリプト言語へ」のマッピングで使用されているのを見てきました。
void DocumentDeserializer::configureDeserializer() {
//XMLMapping<T> is a templated mapping class that
//maps an element name to a field of T and deserializer function.
XMLMapping<Document>::registerElementMapping("track", &Document::tracks, &TrackDeserializer::fromXML);
//Example of registering a new element that doesn't need a special deserializer.
XMLMapping<Document>::registerElementMapping("name", &Document::name);
}
Document* DocumentDeserializer::fromXML(SomeXMLStream* stream) {
Document *doc = new Document();
//Allow the mapper to handle the details.
XMLMapping<Document>::map(stream, doc);
return doc;
}
//similar code for Track and Clip
XML とモデル クラスの結合はコード内に残っていますが、1 つの場所 ( ) で宣言configureDeserializer
され、別の場所 ( ) で実行されfromXML
ます。この分離により、マッピングのリストの最後に 1 行を追加するだけで済むため、後で新しい要素を追加する作業が簡素化されます。
欠点は、未知の量、つまりXMLMapping<T>
クラスです。どの程度の複雑さを処理しなければならないのでしょうか? getter メソッドと setter メソッドを処理する必要がありますか、それともフィールドと直接やり取りする必要がありますか? 日付など、特殊な書式を持つ文字列値をどのように処理しますか? 1 つのフィールドに入力するために 2 つの要素を読み取る必要がある場合、または 2 つのフィールドに入力する 1 つの要素を読み取る必要がある場合はどうすればよいでしょうか? マッピング アプローチは便利ですが、それを機能させるだけでも長く骨の折れる時間がかかる可能性があります。また、最初の 2 つのアプローチでコード化するのが簡単な場合でも、このアプローチでマッピングに変換するのは非常に困難になる可能性があります。
以上が、私が検討する 3 つのアプローチです。これらに基づいて思いつく代替案はたくさんあります (たとえば、Lua のようなスクリプト言語を使用して 2 番目のアプローチでマッピングを管理する)。これがあなたに何か考えるきっかけを与え、最終的にあなたが納得できる解決策を見つけることができることを願っています.