2015-02-10 4 views
1

У меня есть внутренний класс случая, в частности событие из this вопроса, и хочет, чтобы соответствовать его, в том числе внешнего объекта:Scala: Как сопоставить шаблон с окружающим объектом внутреннего класса case?

class Player { 
    var _life = 20 
    def life = _life 

    def gainLife(life: Int) = execute(GainLife(life)) 

    case class GainLife(life: Int) extends Event { 
    def execute() = _life += life 
    } 
} 

Я могу легко написать эффект (частичная функцию), которая заменяет жизненные события для конкретный игрок:

//gain twice as much life 
def effect(player: Player): ReplacementEffect = { 
    case player.GainLife(x) => player.GainLife(x * 2) 
} 

Однако, я не могу сделать то же самое для других игроков. Ближайший я пришел это:

//only you gain life 
def effect2(player: Player): ReplacementEffect = { 
    case evt: Player#GainLife => player.GainLife(evt.life) 
} 

Но 1) это заменяет даже свой собственный lifegain с новым lifegain, 2) Я не могу ссылаться на игрока, который первоначально полученный жизнь в функции и 3) Я m отсутствует прямое соответствие life так же.

Это может быть выражено с помощью континуального независимого типа, как

case Player.GainLife(_player, life) if _player != player => GainLife(player, life) 

В идеале, я хочу что-то вроде

case _player.GainLife(life) if _player != player => player.GainLife(life) 

Возможно ли это каким-то образом, или я могу обойти эту проблему? Или мне нужно прибегать к тому, чтобы сделать GainLife вложенным?

ответ

0

Ближайший я пришел должен определить свой собственный unapply метод:

class Player { 
    self => 

    var _life = 20 
    def life = _life 

    def gainLife(life: Int) = execute(GainLife(life)) 

    case class GainLife(life: Int) extends Event { 
    def player = self 

    def execute() = _life += life 
    } 
} 

object Player { 
    object _GainLife { 
    def unapply(event: Player#GainLife) = 
     Some((event.player, event.life)) 
    } 
} 

Обратите внимание, что называя Player._GainLife объект Player.GainLife вместо вызовет конфликт имен , что является самым важным недостатком здесь. Поэтому я решил сделать этот тип доступен из-за пределов Player имен:

val GainLife = Player._GainLife 

Это позволяет сопрягать с использованием как player.GainLife.unapply и Player._GainLife.unapply в сжатой форме:

//gain twice as much life 
def effect1(player: Player): ReplacementEffect = { 
    case player.GainLife(life) => player.GainLife(life * 2) 
} 

//only you gain life 
def effect2(player: Player): ReplacementEffect = { 
    case GainLife(_player, life) if _player != player => player.GainLife(life) 
} 

//all players gain twice as much life 
def effect3: ReplacementEffect = { 
    case GainLife(player, life) => player.GainLife(life * 2) 
} 

Последние два примера выглядят немного асимметричными, но при необходимости их можно исправить с помощью метода apply.

5

Когда вы определяете класс внутри другого, это означает, что тип специфичен для окружающего класса, поэтому playerA.GainLife не тот же тип, что и playerB.GainLife (это называется зависимым от типа типа), если вы хотите, чтобы он означает то же самое, что вы определяете его в области, которая одинакова независимо от экземпляра: пакет или объект-компаньон вашего класса.

Вы можете прочитать в этом вопросе: What is meant by Scala's path-dependent types?

+0

Думаю, я вижу, в каком направлении вы идете, но это не совсем то, что я хочу. Я доволен «GainLife» в зависимости от затронутого «Игрока», я просто хочу использовать этого игрока, пока он соответствует шаблону. Я добавил, что думаю, что вы предлагаете этот вопрос, но этого я бы хотел избежать, потому что я думаю, что GainLife, зависящий от пути, на самом деле чище, и это единственная причина для добавления объекта-компаньона. –

+0

Кстати, спасибо за ваши ответы;) –

+0

Я нашел способ, который я нахожу довольно удовлетворительным, не могли бы вы предложить некоторые улучшения, если у вас есть? –

Смежные вопросы