オブジェクト間の関係


8

数週間、オブジェクト間の関係について考えてきました。特にOOPのオブジェクトについては考えていません。たとえばC ++では、他のオブジェクトへのアクセスを必要とする構造内でポインターまたはポインターのコンテナーを階層化することでそれを表現することに慣れています。オブジェクトAがにアクセスする必要がある場合、in Bを見つけることは珍しいことではありません。B *pBA

しかし、私はもはやC ++プログラマーではありません。関数型言語を使用してプログラムを作成します。特に、純粋な関数型言語であるHaskellを使用します。ポインター、参照、またはそのようなものを使用することは可能ですが、「Haskell以外の方法で行う」など、私はそれに不思議に思います。

それから私はそれらすべての関係のことについてもう少し深く考え、要点に達しました:

「なぜこのような関係をレイヤー化して表現するのですか?

私はすでにそれについて考えている人々を読んだ(ここ)。私の見解では、明示的なグラフを介して関係を表現する方が、タイプのコアに焦点を合わせ、後でコンビネータを介して関係を表現できるため、はるかに優れています(SQLと少し似ています)。

コア私たちは定義するときにことを意味しA、我々は何を定義するために期待Aされるで作られたそれはない、何に依存します。たとえば、ビデオゲームでは、タイプがある場合Character、それについて話すことTraitは合法です、Skillまたはそのようなことですが、私たちが話すWeaponItemsどうかはそうですか?もうよくわかりません。次に:

data Character = {
    chSkills :: [Skill]
  , chTraits :: [Traits]
  , chName   :: String
  , chWeapon :: IORef Weapon -- or STRef, or whatever
  , chItems  :: IORef [Item] -- ditto
  }

私にとってデザインの点で本当に間違っているように思えます。私はむしろ次のようなものを好みます:

data Character = {
    chSkills :: [Skill]
  , chTraits :: [Traits]
  , chName   :: String
  }

-- link our character to a Weapon using a Graph Character Weapon
-- link our character to Items using a Graph Character [Item] or that kind of stuff

さらに、新しい機能を追加する日が来ると、新しいタイプ、新しいグラフ、リンクを作成できます。最初の設計では、Character型を壊すか、何らかの回避策を使用して拡張する必要があります。

その考えについてどう思いますか?純粋な関数型言語であるHaskellでこのような問題に対処するのに最適な方法は何だと思いますか?


2
ここでは意見の投票は許可されていません。「このアプローチには欠点はありますか?」のように質問を言い換える必要があります。「これについてどう思いますか」の代わりに または「...に最適な方法は何ですか?」C ++にはガベージコレクションや型クラスに似たガベージコレクションがないため(テンプレートを使用しない限り)非常に大きなワームの缶を開きます)、および関数型プログラミングを容易にする他の多くの機能。
ドヴァル2014年

一つの価値の何もあなたのアプローチでは、それは言うことはできないということである型システムを使用しているかどうかCharacter すべき武器を保持することができます。からCharactersWeaponsのマップがあり、そのマップにキャラクターがいない場合、それはキャラクターが現在武器を保持していないか、キャラクターが武器をまったく保持できないことを意味しますか?さらに、が武器を保持できるかどうかがアプリオリにわからないため、不必要な検索を行うことになりますCharacter
ドヴァル2014年

@Dovalキャラクターが武器を保持できないことがゲームの機能である場合、キャラクターレコードにcanHoldWeapons :: Boolフラグを付けて、武器を保持できるかどうか、そしてキャラクターがいないかどうかをすぐに知らせることは意味がないでしょうグラフでは、キャラクターが現在武器を保持していないと言えます。作るgiveCharacterWeapon :: WeaponGraph -> Character -> Weapon -> WeaponGraphだけのように振る舞うidというフラグがあればFalse提供の文字、または使用のためにMaybe失敗したことを表現します。
bheklilr 2014年

私はこれが好きで、率直に言って、あなたが設計しているものがアプリケーションスタイルに非常によく適合していると思います。アプリカティブを使用すると、CRUDスタイルの動作を適切かつ流暢に構成できます。そのため、ビットを個別にモデル化し、検証、永続化、リアクティブイベントなどの構成の間で操作を組み合わせてこれらのタイプを構成できるようにするいくつかのアプリカティブ実装を用意します。発砲など。これは、あなたがそこで提案するものと非常に一致します。グラフはapplicativeでうまく機能します。
ジミーホファ2014年

3
@Doval特にレコードスタイルのデータ型では、複数のコンストラクターがある場合、次のコードは完全に合法であり-Wall、GHCで警告を発行しませんdata Foo = Foo { foo :: Int } | Bar { bar :: String }。次に、チェックを入力してfoo (Bar "")or を呼び出しますbar (Foo 0)が、どちらも例外が発生します。位置スタイルデータコンストラクターを使用している場合、コンパイラーはアクセサーを生成しないため問題はありませんが、大きな構造のレコードタイプの利便性は得られず、使用するボイラープレートが多くなります。レンズのようなライブラリ。
bheklilr 2014年

回答:


2

あなたは実際にはまだ知らないあなた自身の質問に答えました。あなたが質問しているのは、Haskellについてではなく、プログラミング全般についてです。あなたは実際に自分自身に非常に良い質問をしています(kudos)。

手元にある問題への私のアプローチは、基本的に2つの主要な側面に分かれます。ドメインモデルの設計とトレードオフです。

説明させてください。

ドメインモデルの設計:これは、アプリケーションのコアモデルを編成することを決定する方法です。キャラクターや武器などを考えれば、すでにそうしているようです。さらに、それらの間の関係を定義する必要があります。依存するオブジェクトではなく、オブジェクトを定義するというアプローチは完全に有効です。それはあなたのデザインに関するすべての決定のための特効薬ではないので、しかし、注意してください。

これを前もって考えることで間違いなく正しいことをしていることになりますが、ある時点で考えるのをやめて、コードを書き始める必要があります。その後、初期の決定が正しいものであったかどうかがわかります。ほとんどの場合、アプリケーションについての完全な知識がないためです。そして、特定の決定が解決策ではなく問題になっていることに気づいたときに、リファクタリングを恐れないでください。それらの決定を検証し、全体を書き直す必要をなくすことができるように、それらの決定を考えるときにコードを書くことが重要です。

あなたが従うことができるいくつかの良い原則があります、「ソフトウェア原則」をググってください、そしてあなたはそれらの束を見つけます。

トレードオフ:すべてがトレードオフです。一方で、依存関係が多すぎることは悪いことですが、ドメインオブジェクト内ではなく、どこかで依存関係を管理するという複雑な処理に対処する必要があります。正しい決断はありません。直感があるなら頑張ってください。その道をたどり、結果を見ることで多くのことを学びます。

私が言ったように、あなたは正しい質問をしている、そしてそれは最も重要なことです。個人的には私はあなたの推論に同意しますが、あなたはすでにそれについて考えすぎているようです。間違いを恐れないで、間違いを犯してください。これらは非常に価値があり、適切と思われる場合はいつでもコードをリファクタリングできます。


お返事ありがとうございます。それを投稿して以来、たくさんの道を歩みました。AST、バインドされたAST、無料のモナドを試しましたが、今は型クラスで全体を表現しようとしています。実際、絶対的な解決策はありません。私は関係を行うための一般的な方法に挑戦する必要性を感じています。
ファアゾン2014

@phaazonそれはすばらしいことであり、もっと多くの開発者が同様のアプローチを持っていれば幸いです。実験は最高の学習ツールです。プロジェクトで頑張ってください。
アレックス
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.