2

次の Scala クラスのレイアウトを考えています。私はアイテムを表す基本的な特性を持っています - 名前、重量を照会し、次のようなメソッドを呼び出すことによっていくつかのオブジェクト固有のことを行うことができる不変オブジェクトであるべきもののインターフェースですequip:

trait Item {
  def name: String
  def weight: Int
  def equip // ... more abstract methods
}

ケースクラスを手動で作成して実装を作成できますItemが、ある種の「辞書」ベースのアイテムが必要です-つまり、静的マップは型IDから値へのマッピングを保持しnameweightメソッドは格納された型で辞書を照会するだけですID:

object Weapon {
  final val NameDict = Map(1 -> "short sword", 2 -> "long sword")
  final val WeightDict = Map(1 -> 15, 2 -> 30)
}
case class Weapon(typ: Int) extends Item {
  import Weapon._
  def name = NameDict(typ)
  def weight = WeightDict(typ)
  def equip = ... // implementation for weapon
}

ここまでは順調ですね。Weapon(1)「ショート ソード」のアイテム オブジェクトを参照するために使用できます。Armorただし、まったく同じ実装を使用するnameweight、完全に異なるequip他の抽象メソッド実装を使用するなどの他の項目タイプにも同じ基本原則が適用されます。次に例を示します。

object Armor {
  final val NameDict = Map(3 -> "bronze armor", 4 -> "iron armor")
  final val WeightDict = Map(3 -> 100, 4 -> 200)
}
case class Armor(typ: Int) extends Item {
  import Armor._
  def name = NameDict(typ)
  def weight = WeightDict(typ)
  def equip = ... // implementation for armor
}

にかなり似てWeaponいますね。共通のパターン (つまり、コンパニオン オブジェクト ディクショナリと共通のtyp値でのルックアップの実装) を次のように除外したいと思います。

abstract class DictionaryItem(typ: Int) extends Item {
  def name = ???.NameDict(typ)
  def weight = ???.WeightDict(typ)
}

object Weapon {
  final val NameDict = Map(1 -> "sword", 2 -> "bow")
  final val WeightDict = Map(1 -> 15, 2 -> 30)
}
case class Weapon(typ: Int) extends DictionaryItem(typ) {
  def equip = ... // implementation for weapon
}

しかしWeapon.NameDict、親クラスから子のコンパニオン オブジェクト ( など) を参照するには???.NameDict(typ)どうすればよいですか? そのような問題に対するより良い、より Scala 風のアプローチはありますか?

4

3 に答える 3

4

マップ内のアイテムの名前と重量を調べる代わりに、ケース クラスのインスタンスを利用できます。実際、このアプローチを採用する正当な理由がいくつかあります。

  1. 複雑さの回避: 十分に単純です。継承がコンパニオン クラスでどのように機能するかについて、この質問を投稿する必要はなかったかもしれません。
  2. 繰り返しの回避: ケース クラスを使用すると、項目を 1 回定義するだけで済みます。
  3. エラーの回避: 合成キーを使用すると、名前辞書と重み辞書の両方で正しいキーを使用する必要があります。項目 2 を参照してください。
  4. 慣用的なコードを書く: Scala はオブジェクト指向であり、機能的でもあります。この場合、アイテムの周りに自分の向きを合わせます。
  5. オーバーヘッドの回避: 1 回のルックアップでアイテムのすべてのプロパティを取得します。

以下は、Item が主役の別のテイクです。

package object items {
  val weapons = Weapon("halbred", 25) :: Weapon("pike", 35) :: Nil
  val armor = Armor("plate", 65) :: Armor("chainmail", 40) :: Nil
  val dictionary = (weapons ::: armor) map(item => item.name -> item) toMap
}

名前と重みは、マップ内の値としてパッケージ化されるのではなく、インスタンスにパッケージ化されることに注意してください。また、管理するインデックスはありません。ただし、何らかの理由でインデックスが必要な場合は、装備リスト内のアイテムの位置を簡単に使用したり、アイテム トレイトにプロパティを追加したりできます。

そして、次のように使用できます。

> items dictionary("halbred")        // Weapon(halbred, 25)
> items dictionary("plate") weight   // 65

最後に 1 つ注意してください。Scala ワードの最後のキーワードは時代錯誤のようなものです。Scala で val を定義すると、それはすでに不変です:なぜ「private val」と「private final val」は異なるのですか?

于 2012-12-01T07:02:48.357 に答える
2

som-snytt の言うとおりです。andArmorオブジェクトWeaponをトレイトに拡張するDictionaryだけです。NameDictWeightDictDictionaryItem(typ: Dictionary)

これは非常に複雑な方法のように思えますが、すべてのプロパティを数値で検索すると、間違いを犯しやすく、リファクタリングが困難になります。より自然な方法は、実際のオブジェクトを次のようにマップに配置することです。

case class Weapon(name: String, weight: Int, equip: ...) extends Item
case class Armor(name: String, weight: Int, equip: ...) extends Item

val ShortSword = Weapon(name = "short sword",
                        weight = 15,
                        equip = ...)

val LongSword = Weapon("long sword", 20, ...)

val Weapons = Map(
  1 -> ShortSword,
  2 -> LongSword
)

たとえば、Weapons(1).nameではなくWeapon.NameDict(1). または、単に書くこともできますShortSword.name。または、インデックスを介してのみ参照する必要がある場合は、アイテムをマップで直接定義できます。

于 2012-12-01T04:06:06.697 に答える
1

欲しいと思いますobject Weapon extends Dictionary。私はかつてそれをアーモリーと呼んでいました。武器の工場を意味します。しかし、任意の -ory または -ary で十分です。また、整数型を列挙型に変更します。次に、Weapon(Kinds.Sword) を実行すると、Sword を返すように Weapon.apply(kind: Kind) を定義します。

コンパニオン オブジェクトによって実装されるトレイトのファクトリ メソッドのこのパターンは、教育的なコレクション フレームワークによって使用されます。

于 2012-12-01T01:37:49.727 に答える