Во-первых, нам нужно исправить ошибку компиляции в определении cards
поля.
Обратите внимание, что в Scala вам обычно не нужно объявлять поля. Основные параметры конструктора уже являются полями! Итак, это можно записать проще:
class Hand(cards : List[Card]) {
if (cards.length>5)
throw new IllegalArgumentException(
"Illegal number of cards: " + mycards.length);
Теперь у нас проблема с изменчивостью. Если вы хотите программировать в функциональном стиле, все должно быть неизменным, поэтому «конструктор полной руки» вообще не функционален: он имеет 6 побочных эффектов, последняя из которых не компилируется.
В функциональной настройке объект нельзя изменить после завершения его конструктора, поэтому весь код после this(Nil)
бесполезен. Вы уже сказали, что cards
- это Nil
, что еще вы хотите ?! Итак, все вычисления должны произойти за до вызова главного конструктора. Мы хотели бы удалить this(Nil)
сверху и добавить this(sortCards(cardBuffer.toList))
снизу. К сожалению, Scala не позволяет этого. К счастью, он дает больше возможностей для достижения того же уровня, чем java: во-первых, вы можете использовать вложенный блок следующим образом:
this({
val cardBuffer = ... /* terrible imperativeness */
sortCards(cardBuffer.toList)
})
во-вторых, вы можете использовать apply
метод вместо конструктора:
object Hand {
def apply(a : String, b : String, c : String, d : String, e : String) = {
val cardBuffer = ... /* terrible imperativeness */
new Hand(sortCards(cardBuffer.toList))
}
}
Теперь давайте начнем избавляться от императива ListBuffer
. Первым улучшением будет использование var
типа List[Card]
. Если сделать изменчивость более локальной, это поможет удалить ее позже:
// assign cards
var cards = Nil
if ( e!=null ) cards = new Card(e) :: cards
if ( d!=null ) cards = new Card(d) :: cards
if ( c!=null ) cards = new Card(c) :: cards
if ( b!=null ) cards = new Card(b) :: cards
if ( a!=null ) cards = new Card(a) :: cards
sortCards(cards)
Хорошо, теперь мы можем видеть, что именно мы мутируем, и можем легко удалить эту изменчивость:
val fromE = if ( e!=null ) new Card(e) :: Nil else Nil
val fromD = if ( d!=null ) new Card(d) :: fromE else fromE
val fromC = if ( c!=null ) new Card(c) :: fromD else fromD
val fromB = if ( b!=null ) new Card(b) :: fromC else fromC
val fromA = if ( a!=null ) new Card(a) :: fromB else fromB
sortCards(fromA)
Теперь у нас довольно много дублирования кода. Давайте удалим это методом грубой силы (найдите длинный дублирующий кусок кода и извлеките функцию)!
def prependCard(x : String, cards : List[Card]) =
if ( x!=null ) new Card(x) :: cards else cards
val cards = prependCard(a, prependCard(b,
prependCard(c, prependCard(d,
prependCard(e, Nil)
))
))
sortCards(cards)
Следующим, очень важным преобразованием было бы заменить пустые ссылки на значения типа Option
или полностью удалить концепцию пустой карты.
Обновление:
По запросу я добавляю пример использования метода apply
. Обратите внимание, что он объявлен в object Hand
, а не class Hand
, поэтому ему не нужен экземпляр класса (это похоже на статический метод в Java). Мы просто применяем объект к параметрам: val hand = Hand("5S", "5S", "5S", "5S", "5S")
.