10

かなり長い間、私はアプリケーションに取り組んできました。プログラミングは単なる趣味であるため、このプロジェクトはすでに時間がかかりすぎていますが、それは重要なことではありません。私は今、すべての「問題」を解決するのが非常に困難になっているところにいます。そして、私はコードをリファクタリングすることを考えています、しかしそれは「完全な」書き直しをもたらすでしょう。

問題と、現在どのように解決したかを説明します。基本的に私はデータを持っています、そして私はこのデータの上で物事を起こさせます(私はすべてのプログラムについてよく説明しましたか?)。何が起こるか:

データ->ビューアに表示を要求->ビューアは実際のデータに基づいてデータを表示ビューアはユーザー入力を返します->データ->「エグゼキュータ」に実行を要求->新しいデータ

ここに画像の説明を入力してください

今ではこれは非常にうまく機能していました。私はもともと「たとえば、コマンドプロンプトをqtやwindowsで変更するか、外部(C#)を使用して、このプログラムを呼び出すだけかもしれない」と考えていました。

しかし、プログラムが成長するにつれて、それはますます面倒になりました。最も重要なことは、データが何であるか、そしてさらに重要なことに、データがどこにあるかに応じて、データがさまざまな方法で表示されることです。そこで、ツリーに戻って、親の行が何であるかを「追跡」するために何らかの方法で追加しました。次に、一般の視聴者は、最も具体的な実際のウィジェットを検索します。最適な場所。

問題は、新しい「データ」を更新するときに始まります。ビューア、セーバーなど、すべてのアセットを確認する必要があります。チェックメカニズムを更新すると、多くのエラーが発生しました。今また?」

これで、これを完全に入れ替えることができます。そして、一般的なビューアを呼び出すツリーデータ構造の代わりに。OOの「内部」ツリー機能を使用します。ノードは子になります(新しいビューアまたは保存メカニズムが必要な場合は、新しい子が形成されます)。

これにより、ツリー内の場所をチェックする難しいチェックメカニズムが削除されます。ただし、他のすべてのワームの缶を開く可能性があります。そして、私はこれについていくつかのコメントが欲しいですか?ビューアを完全に分離しておく必要があります-データのチェックに問題がありますか?または、新しいアプローチの方が優れていますが、データと実行を1つのノードに結合します。(したがって、qtからcli / C#に変更したい場合は、ほとんど不可能になります)

ここに画像の説明を入力してください

最終的にどのような方法を追求する必要がありますか?また、他に何かできることはありますか?ビューアを分離したまま、表示するウィジェットを確認するためのチェックを行わないようにするには?

いくつかの「コード」と私のプログラムがどのように機能するかを示すためだけに編集してください。すでに言ったように、これが良いかどうかはわかりませんが、方法論のかなりのクラスターファックになっています。

これは、いくつかの「ゲームメーカープロジェクト」をマージすることを目的としています(GM:studioには奇妙なことにその機能がないため)。Gamemakerプロジェクトファイルは、単にxmlファイルのセットです。(他のxmlファイルへのリンクのみを含むメインxmlファイル、および各リソース(オブジェクト、スプライト、サウンド、ルームなど)のxmlファイル)。ただし、ブーストプロパティツリーやqtなどで実際に読み取ることができない「癖」がいくつかあります。1)ファイルの特定の部分で属性/子ノードの順序が非常に重要です。2)空白は無視されることがよくありますが、他の点では空白を保持することが非常に重要です。

そうは言っても、ノードがまったく同じであるポイントもたくさんあります。背景がどのように持つことができるか、<width>200</width>そして部屋もそれを持つことができるように。しかし、ユーザーにとって、彼が話している幅は非常に重要です。

とにかく、「一般的なビューア」(AskGUIFn)には、これを処理するための次のtypedefがあります。

    typedef int (AskGUIFn::*MemberFn)(const GMProject::pTree& tOut, const GMProject::pTree& tIn, int) const;
    typedef std::vector<std::pair<boost::regex, MemberFn> > DisplaySubMap_Ty;
    typedef std::map<RESOURCE_TYPES, std::pair<DisplaySubMap_Ty, MemberFn> > DisplayMap_Ty;

「GMProject::pTree」がツリーノードである場合、RESOURCE_TYPESは、現在のリソースの種類(スプライト、オブジェクトなど)を追跡するための定数です。「memberFn」は、ここでは単にウィジェットをロードするものになります。(もちろん、AskGUIFnだけが一般的なビューアーではありませんが、これは、他の「自動」-上書き、スキップ、名前変更-ハンドラーが失敗した場合にのみ開かれます)。

次に、これらのマップがどのように初期化されるかを示します(名前空間「MW」のすべてがqtウィジェットです)。

AskGUIFn::DisplayMap_Ty AskGUIFn::DisplayFunctionMap_INIT() {
    DisplayMap_Ty t;
        DisplaySubMap_Ty tmp;

        tmp.push_back(std::pair<boost::regex, AskGUIFn::MemberFn> (boost::regex("^instances "), &AskGUIFn::ExecuteFn<MW::RoomInstanceDialog>));
        tmp.push_back(std::pair<boost::regex, AskGUIFn::MemberFn> (boost::regex("^code $"), &AskGUIFn::ExecuteFn<MW::RoomStringDialog>));
        tmp.push_back(std::pair<boost::regex, AskGUIFn::MemberFn> (boost::regex("^(isometric|persistent|showcolour|enableViews|clearViewBackground) $"), &AskGUIFn::ExecuteFn<MW::ResourceBoolDialog>));
        //etc etc etc
    t[RT_ROOM] = std::pair<DisplaySubMap_Ty, MemberFn> (tmp, &AskGUIFn::ExecuteFn<MW::RoomStdDialog>);

        tmp.clear();
        //repeat above
    t[RT_SPRITE] = std::pair<DisplaySubMap_Ty, MemberFn>(tmp, &AskGUIFn::ExecuteFn<MW::RoomStdDialog>);
    //for each resource type.

次に、ツリーデータ構造が一般のビューアに表示を希望することを通知すると、ビューアは次の機能を実行します。

AskGUIFn::MemberFn AskGUIFn::FindFirstMatch() const {
    auto map_loc(DisplayFunctionMap.find(res_type));
    if (map_loc != DisplayFunctionMap.end()) {
        std::string stack(CallStackSerialize());
        for (auto iter(map_loc->second.first.begin()); iter != map_loc->second.first.end(); ++iter) {
            if (boost::regex_search(stack, iter->first)) {
                return iter->second;
            }
        }
        return map_loc->second.second;
    }

    return BackupScreen;
}

そして、これは問題が率直になり始めたところです。関数はCallStackSerialize()コールスタックに依存します。ただし、そのcall_stackは「ハンドラー」内に格納されます。すべてがハンドラーから始まるので、そこに保存しました。この「call_stack」をどこに保存すればよいのかよくわかりません。何が起こっているかを追跡する別のオブジェクトを導入しますか?親をノード自体と一緒に保存するルートを試してみました。(コールスタックの必要性を防ぎます)。しかし、それは私が望んでいたほどうまくいきませんでした。各ノードには、その子ノードを含むベクトルがあります。したがって、ポインタを使用して親のメモを指すことは問題外です...(PS:別の質問でこれを修正する必要があるかもしれません..)

4

4 に答える 4

2

この複雑な位置チェックメカニズムをビューアから専用のクラスにリファクタリング/書き換えることは理にかなっているため、プログラムの他の部分に影響を与えることなくソリューションを改善できます。これを呼びましょうNodeToWidgetMap

アーキテクチャIMOの良い点であるModel-View-Controller
アーキテクチャ に向かっているようです。ツリー構造とそのノードはモデルであり、ビューアーと「ウィジェット」はビューであり、ノードに応じてウィジェットを選択するロジックはコントローラーの一部になります。

主な問題は、特定のノードNに対していつどのようにウィジェットw Nを選択するか、およびこの選択をどのように保存するかということです。

NodeToWidgetMap:いつ選択する
かノードが移動されても 、w Nがその存続期間中に変化しないと想定できる場合は、ノードを作成するときに正しく選択できます。それ以外の場合は、場所(またはXMLを介したパス)を知る必要があり、その結果、ノードを要求するときにノードの親を見つける必要があります。

親ノード
の検索 私の解決策は、おそらくを使用して、ノードインスタンス自体の代わりにへのポインタをboost::shared_ptr格納することです。これには欠点があります。たとえば、ノードをコピーすると、再帰を使用してサブツリーのディープコピーを作成する独自のコピーコンストラクターを実装する必要があります。(ただし、移動しても子ノードには影響しません。)

親ノードにそれぞれ祖父のベクトルに触れるたびに子ノードを最新の状態に保つなどの代替手段が存在します。Node::findParentOf(node)または、特定のノードが特定のノードの子としてのみ(または頻繁に)検出されることを認識している関数を定義できます。これは野蛮ですが、小さな木には適度にうまく機能し、あまりうまくスケーリングしません。

NodeToWidgetMap:選択方法紙にw N
を選択する方法のルールを、おそらく部分的に書き留めてみてください。次に、これらのルールをC++に変換してみてください。これはコードの観点からは少し長くなる可能性がありますが、理解と保守が容易になります。

現在のアプローチは、XMLパス(スタック)を照合するために正規表現を使用することです。

私のアイデアは、エッジがXML要素名でラベル付けされ、ノードがどのウィジェットを使用するかを示すルックアップグラフを作成することです。このようにして、XMLパス(スタック)はグラフを通るルートを記述します。次に、グラフを明示的にモデル化するかどうか、または関数呼び出しのグループを使用してこのグラフをミラーリングできるかどうかが問題になります。

NodeToWidgetMap:選択肢を保存する場所
一意の数値IDを各ノードに関連付け、ノードIDからNodeToWidgetMap内のウィジェットへのマップを使用してウィジェットの選択肢を記録します。

書き換えとリファクタリング
書き換える場合、ホイールを書き換えるのではなく、プログラムに集中するために、Qtなどの既存のフレームワークにうまく結び付けることができます。各プラットフォームの特殊性を抽象化するよりも、適切に作成されたプログラムをフレームワークから別のフレームワークに移植する方が簡単な場合があります。Qtは、MVCアーキテクチャの経験と十分な理解を得るための優れたフレームワークです。

完全に書き直すと、すべてを再考する機会が得られますが、最初から始めて、かなりの時間新しいバージョンがないというリスクがあります。そして、あなたが終了するのに十分な時間があるかどうか誰が知っていますか?既存の構造をリファクタリングすることを選択した場合は、段階的に改善し、各段階の後に使用可能なバージョンを用意します。しかし、古い考え方にとらわれたままになるリスクはわずかです。書き直しを行うと、ほとんどすべてを再考する必要があります。したがって、どちらのアプローチにもメリットがあります。プログラミングを楽しんでいる場合は、書き直します。より多くのプログラミング、より多くの喜び。

于 2012-11-23T09:37:16.703 に答える
1

プログラミングの世界へようこそ!
あなたが説明するのは、アプリケーションの典型的なライフサイクルであり、小さなシンプルなアプリとして始まり、その後、メンテナンスができなくなるまで、ますます多くの機能を取得します。この最後の崩壊段階で私が見たプロジェクトの数を想像することはできません!
リファクタリングする必要がありますか?もちろんそうです!いつも!すべてを書き直す必要がありますか?よくわかりません。
実際、良い解決策はサイクルごとに作業することです。コーディングに必要なものを設計し、コーディングし、より多くの機能が必要になり、この新しい機能を設計し、コードをリファクタリングして新しいコードを統合できるようにします。このようにしないでください。そうすれば、リファクタリングよりもリライトする方が安価なポイントに到達します。この本を入手してください:リファクタリング-マーティンファウラー。あなたがそれを好きなら、これを手に入れてください:パターンへのリファクタリング。

于 2012-11-16T14:36:07.893 に答える
0

Robert Martinsの「C#でのアジャイルの原則、パターン、および実践」のコピーを購入することをお勧めします。彼は、このようなメンテナンスの問題を克服する方法を示すいくつかの非常に実用的なケーススタディを調べます。

于 2012-11-22T00:36:20.580 に答える
0

すでにPedro NF述べたように、MartinFowlerの「リファクタリング」はそれに慣れるための素晴らしい場所です。

于 2012-11-21T23:49:08.333 に答える