3

私は卓上ゲームのデータを管理する単純なプロジェクトを行っていますが、ほとんどの場合、正しいコーディングの経験を積むために使用しています。

5 つのクラスが密結合している状態になりましたが、すべてをそのままにしておくか、リファクタリングするかはわかりません。

私が持っているのは基本的にこれです:

  • class ShipTemplate: このクラス (c++ テンプレートとは関係ありません) には、すべての定数メンバーがあり、船のカテゴリに関する基本的な情報が含まれています。
  • class TemplateSet: このクラスには、現在ビルドに使用できるすべての ShipTemplates が含まれており、名前があります。各プレーヤーがいつでも利用できるテクノロジーを表すため、スタンドアロンである必要があります。そのため、さまざまなセットをさまざまな時間に保存/ロードできます。
  • class Ship: このクラスは、ロードアウト、名前などを含む完全な船を表します。基本的な機能を参照するために、クラスが変更することを許可されていない ShipTemplate への const 参照が含まれています。ShipTemplate を拡張することもできますが、どの Ship が特定の基になる ShipTemplate を持っているかを追跡したかったので、このようにする方が簡単に思えました。
  • class Fleet: このクラスには船のリストが含まれており、名前とその他の情報が含まれています。これには、その中のすべての船のコストの合計に等しいコスト変数が含まれている必要があります。
  • class Deployment: このクラスには、プレイヤーが利用できるすべての船、艦隊、および TemplateSet へのポインタが含まれています。また、利用できなくなったものの、すでに建造された Ship でまだ使用されている ShipTemplates を追跡する必要もあります。これには、プレイヤーが利用できるすべての船のコストの合計に等しいコスト変数が含まれている必要があります。ある艦隊から別の艦隊への船の移動を管理する必要があります。どの船が特定の艦隊内にあるか、またはどの船が特定の ShipTemplate を持っているかを調べる必要があります。

残念ながら、すべてのクラスは他のすべてのクラスとかなり絡み合っています。さまざまなアプローチを考えましたが、そのうちの 1 つでも正しいかどうかはわかりません。

  • あらゆる場所でステートメントを使用friendして、1 つのクラスが何かを変更した場合に、他のすべてを正しく更新できるようにします。
  • のような非常に長い名前を使用Deployment::modifyShipThrustInFleetし、すべてを処理する Deployment クラスを介してのみ変更を許可します。
  • TemplateSets と Fleets を削除し、Deployment 内でそれらを表現して、「正確性」ルールを破ることなくコスト値/ポインターを正しく更新できるようにします。これもまた、システムへのすべての変更が Deployment を通過する必要があることを意味します。
  • 上位クラスへのポインターを下位クラスに挿入します。たとえば、船で何かを変更すると、展開と艦隊の両方のコストを自動的に更新できます。

私が見たことのない他の解決策がありますか、それとも読みやすく、管理しやすいコードを実現するのに役立つより多くのクラスへのリファクタリングがありますか?

4

2 に答える 2

2

ほんの少しの考え。

オブジェクト指向で考えるとき、実際のオブジェクトについて考えると、プログラムに記述すべきビューがレイアウトされます。そして、実生活におけるオブジェクトとは何ですか?これは、特定の機能を持ち、いくつかの機能を公開する「もの」です。

たとえば、ShipTemplate は船の設計図です。サイズ、レイアウト、部品の種類と数量 (DieselEngine、SteamEngine、AntiAircraftGun、UnderwaterMines など)、およびそれらの相互接続方法を定義する必要があります。

もう一方の船は設計図に従って構築されていました - すべての部品インスタンスが必要です。たとえば、2 つの DieselEngine と 3 つの AntiAircraftGun があるとします。そして、その船は設計図を継承していないのは正しいです。ブループリントは船の説明であり、親ではありません。

現在、オブジェクトの各タイプ (設計図、パーツ、船) には、独自のプロパティと機能があります。たとえば、各エンジンはある程度の燃料を消費し、船の速度をある程度まで上げることができます。これらの機能を備えたエンジンの基本クラスを持たないのはなぜですか? (継承)。同じことが銃にも当てはまります (ShipWeapon と呼びましょう)。もちろん機雷と対空砲には大きな違いがありますが、どちらも砲であり、船に搭載可能であり、重量、弾薬の種類、再装填時間、弾薬容量、銃が作動しているかどうかがあります。

これらはオブジェクトのいくつかのプロパティです。機能性はどうですか?OO 設計の他の重要な概念は、各オブジェクトには、それで実行できるいくつかの機能がある (カプセル化されている) ことです (オブジェクトの状態を変更する場合と変更しない場合があります)。たとえば、ShipWeapon にはメソッド Fire() が必要です。これは、おそらく弾薬の量を決定する必要があります。または、Aim(sometarget) で最初にターゲットを設定してみてください。一方、エンジンには Start()、Stop()、SetSpeed(速度) があります。これらはオブジェクトとその状態で内部的に機能することに注意してください。船には SetCourse(方向、速度) があり、必要な出力でエンジンを始動し、舵の向きを変えることができます。また、船には Colide(船) がある場合があります。そして、Hit(typeofattackinggun) は、船のすべての部分を反復し、一部をランダムに損傷させます (そして、IsOperating を銃に設定するか、エンジンの 1 つをオフにするなど)。

ご覧のとおり、OO アプローチを設計する際に、多くの詳細に入ることができます。また、いつ停止するかを知ることも非常に役立ちます。プログラムが機能するために本当に必要な詳細(または精度)です。

また、すべての船を保持するグローバル ワールドが存在する可能性もあります。等々..

プログラムの他の部分であるインフラストラクチャがあります。データ オブジェクト (船、ワールド、プレイヤー) がどのように管理されているか、それらがどのように相互に認識し相互作用しているか。たとえば、オブジェクトとしての各船はグローバルマップで観測でき、各船は動きについて通知します(オブザーバーパターン)。または、グローバル ワールドは、グローバル クロックに基づいて、一定の時間間隔で各船の状態を照会します。または...

私が言おうとしていたのは、オブジェクト指向の主な原則 (カプセル化、継承、ポリモーフィズム) に固執することだと思います。また、オブジェクト指向設計、設計パターンなどに関する文献がたくさんあり、役に立ちます。Wikiエントリは少し「学術的」ですが、主な定義があり、考えさせられます:) SOLIDも見てください

PS。そして、すべてを単一のクラスで行うのは、通常、設計が悪いことを示しています。

于 2012-12-03T20:31:50.343 に答える
1

さまざまなデータを表現したい方法を記述したので、完全な関係を定義する前に、「 protocol」を定義して記述を完成させます。

各クラスは他のクラスに何ができるでしょうか? 目標を達成するために必要な方法と方法間のルールは何ですか?

クラスが互いにどのように作用するかを定義すると、何がプライベートになるのか、何がパブリックになるのか、当事者間にどのレベルの友情が存在する必要があるのか​​がわかります。

あなたの場合ではないかもしれませんが、通常、複雑な関係が存在する場合、考えられるパターンの 1 つは、さまざまなオブジェクトに「送信」できるアクションを公開する「通信バス クラス」を使用することです。インターフェースであり、バス自体 (バスのみ) のフレンドです。

編集

Svalorzen のコメントに続いて:

それはあなたがそれを見る側に依存します。実際、これにより複数レベルの「プライバシー」が導入され、クラス自体よりも広い単位でカプセル化を実装できるようになります。これが良いか悪いかは、慣用句ではなく文脈の問題です。

すべてがプライベート (他の誰のためでもない) またはパブリック (誰のためでもある) のクラスだけを持つ代わりに、「クラブ」である「カプセル」(「「バス」を友達として持つクラスのクラブ」) があります。そして、「クラブ マネージャー」は真の「パブリックに対するフィルター」(したがって真の OOP オブジェクト) であり、より多くのクラスのプライベート パーツと同時に相互作用する必要のある特定のメソッドを、クラブ内でのみ実行できるようにします。

「友情」の否定は、技術とツールを混同し、OOP オブジェクトを C++ クラスと同じにする誤解にすぎません。それは - 一般的に言えば - FASLE IDIOM です。C++ クラスは、OOP オブジェクトよりも小さな単位にすることができます (ピンプルのイディオムを考えてみてください: そこにある「オブジェクト」とは何ですか?)。クラスが別のクラスの友達になることができるという事実は、それが誰の友達にもならないため、プライベートな部分は公開されません。カプセル化が「プライベート」と同じように適用される、別のレベルのプライバシーを定義しているだけです。より広いグループに適用されるだけです。その「より広いグループ」は、OOPに関して、非フレンドクラスが果たすのと同じ役割を果たします。

「友人がカプセル化を破る」という誤解は、OOP の概念とは何の関係もありません。これは、OOP が Java で実装されている方法に関係しています。これは、C++ とはまったく異なる言語です。C++ では、friendsip は、クラス、構造体、テンプレート、継承、メンバーシップなどと同じように、「物事をグループ化する」ための構成要素にすぎません。

言語哲学が一方向のみであると定義されている場合、Java とは異なり、どの OOP 関係 (構成、継承、リンク...) をどの C++ コンストラクトにマップする必要があるかは、言語自体とその標準によって定義されていません。図書館。

「OOP オブジェクト = C++ クラス」というマッピングは、過去から継承された一般的な文化的誤解にすぎません。C++ にはテンプレートもラムダも友情もなく、クラス以上のものはありません (実際には「C with classes」と呼ばれていました)。 OOP 階層を実装する唯一の方法は、クラスを使用することでした。これは、当時の C++ 構造を使用して階層関係を作成する唯一の方法だったためです。

最近では、C++ メンバーと「OOP 継承」の暗黙的な変換と「OOP メンバーシップ」の C++ プライベート継承を使用して、OOP システムを実装することもできます。または、実行時の動作を定義する「クラスのクラスター (または labdas のマット)」を使用して OOP オブジェクトを実装できます (std::locale および関連するファセットを考えてください)。

OOP オブジェクト == C++ クラスのイディオムで設計を開始することは、実際には、C++ がプログラム設計に追加する 2 つの自由度を切り捨て、10 年以上前の C++ が何であったかということに頭を悩ませることになります。

于 2012-12-03T19:44:50.953 に答える