次のコードを書いたとしましょう:
ゲームモジュール
module Game where
import Player
import Card
data Game = Game {p1 :: Player,
p2 :: Player,
isP1sTurn :: Bool
turnsLeft :: Int
}
プレーヤーモジュール
module Player where
import Card
data Player = Player {score :: Int,
hand :: [Card],
deck :: [Card]
}
とカードモジュール
module Card where
data Card = Card {name :: String, scoreValue :: Int}
次に、プレイヤーが順番に手札からカードを引いたりプレイしたりして、ゲームのターンがなくなるまでスコアにボーナスを追加するロジックを実装するコードをいくつか書きます。
しかし、このコードを完成させてみると、私が書いたゲーム モジュールはつまらないものであることに気付きました!
カードゲームをリファクタリングして、カードをプレイすると、単にスコアを追加するのではなく、カードがゲームを任意に変換するようにしたいと考えています。
したがって、Card
モジュールを次のように変更します
module Card where
import Game
data Card = Card {name :: String,
onPlayFunction :: (Game -> Game)
scoreValue :: Int}
もちろん、モジュールのインポートがサイクルを形成します。
この問題を解決するにはどうすればよいですか?
些細な解決策:
すべてのファイルを同じモジュールに移動します。これは問題をうまく解決しますが、モジュール性を低下させます。同じカード モジュールを後で別のゲームに再利用することはできません。
モジュール維持ソリューション:
タイプ パラメータを に追加しますCard
。
module Card where
data Card a = {name :: String, onPlayFunc :: (a -> a), scoreValue :: Int}
に別のパラメーターを追加しますPlayer
。
module Player where
data Player a {score :: Int, hand :: [card a], deck :: [card a]}
に 1 つの最終的な変更を加えると、次のようになりGame
ます。
module Game where
data Game = Game {p1 :: Player Game,
p2 :: Player Game,
}
これによりモジュール性が維持されますが、データ型にパラメーターを追加する必要があります。データ構造がこれ以上深くネストされている場合、データに大量のパラメーターを追加する必要があり、この方法を複数のソリューションに使用する必要がある場合は、扱いにくい数の型修飾子が必要になる可能性があります。
では、このリファクタリングを解決するための他の有用な解決策はありますか、それともこれらの 2 つのオプションしかありませんか?