2015-09-24 1 views
3

Рассмотрим следующий код:Действительно ли это (правда) плохо использовать case-классы для изменяемого состояния?

case class Vector3(var x: Float, var y: Float, var z: Float) 
{ 
    def add(v: Vector3): Unit = 
    { 
    this.x += v.x 
    this.y += v.y 
    this.z += v.z 
    } 
} 

Как вы можете видеть, case class держит изменяемого состояния. Очень не рекомендуется делать это, и обычно я соглашаюсь и абсолютно придерживаюсь этого «правила», но здесь идет вся история.

Я использую Scala, чтобы написать небольшой 3d-игровой движок от scratch. Поэтому сначала я подумал о том, чтобы использовать (гораздо более) функциональный стиль, но потом сборщик мусора слишком часто пинал.

Подумайте об этом на мгновение: у меня есть десятки и десятки сущностей в тестовой игре. Все они имеют положение (Vector3), ориентацию (Vector3), масштаб (Vector3) и множество матриц. Если бы я собирался функционировать в этих классах (Vector3 и Matrix4) и сделать их неизменными, я бы возвращал сотни новых объектов в каждый кадр, что приводило к огромной ошибке fps, потому что, давайте посмотрим правде в глаза, GC имеет свои применения, но в игровой движок и с OpenGL ... не так много.

Vector3 был классом раньше, но теперь это класс case, потому что где-то в коде мне нужно сопоставить шаблоны для него.

Итак, действительно ли это , что плохо использовать класс case, который содержит изменяемое состояние?

Просьба сделать не Включите это в дискуссию «Почему вы даже используете Scala для такого проекта?» Я знаю, что там может быть лучшими альтернативами, но мне не интересно писать (еще один) движок на C++, и я тоже не хочу погружаться в Rust (пока).

+3

Я не знаю, если есть хороший ответ на этот вопрос. Как вы сказали, крайне не рекомендуется создавать классы case с изменчивым состоянием, но, на мой взгляд, это соглашение, о котором вам следует заботиться, когда вы реализуете библиотеку. В таком случае это может быть довольно неприятно, если вы можете изменить состояние класса case, где его никто не ожидает. Но если вы хорошо документируете это, и это очень важно для необходимой производительности, вы можете использовать доступные функции. –

+2

Просто внешнее мнение: когда дело доходит до низкоуровневого программирования, в дикой природе есть бесчисленные примеры, выходящие за рамки конвенций и установленных способов ведения дел. Верьте или нет, на C++ есть соглашения и передовая практика, но эти хорошие практики и читаемость часто приносятся в жертву для оптимизации производительности. – Ruslan

+0

взгляните на этот вопрос: http://stackoverflow.com/questions/4653424/what-are-the-disadvantages-to-declaring-scala-case-classes – anquegi

ответ

5

Я бы сказал, что использовать классы case с изменчивым состоянием плохо, но только потому, что они переопределяют методы equals и hashCode. Где-то в вашем коде вы можете проверить, a == b и узнать, что они равны. Позже они могут быть разными, потому что они изменяемы. По крайней мере, они опасны для использования в сочетании с коллекциями на основе хэшей.

Однако, похоже, вам не нужны все функциональные возможности класса case. То, что вам действительно кажется необходимым, - это экстрактор для сопоставления шаблонов, так почему бы не определить его? Кроме того, статический завод apply и читаемое сообщение toString могут быть удобными, поэтому вы можете их реализовать.

Как насчет:

class Vector (var x: Float, var y: Float, var z: Float) { 
    override def toString = s"Vector($x, $y, $z)" 
} 

object Vector { 
    def apply(x: Float, y: Float, z: Float) = new Vector(x, y, z) 

    def unapply(v: Vector): Option[(Float, Float, Float)] = Some((v.x, v.y, v.z)) 
} 
+0

Было бы лучше просто использовать готовую к использованию линейную алгебру коллекции (http: // stackoverflow.com/questions/12449427/does-scalala-обеспечить-прямо-путь-вставить-вектор-в-матрицу) вместо определения собственного вектора – ayvango

+0

Спасибо, у меня уже есть удобный метод toString. Применить все, что мне нужно, чтобы сделать сопоставление шаблонов? Думаю, мне понадобится 'equals' и' hashCode' тоже? – Sorona

+0

@ayvango: Я хочу узнать что-то новое. Чтобы узнать что-то новое, я должен экспериментировать. В этом случае я хочу улучшить свою Scala и изучить еще одну компьютерную графику в одном и том же режиме. Я уже написал двигатель на C++, но там я использовал GLM. Я также использовал libgdx и тому подобное в прошлом. Теперь я хочу погрузиться глубже, и я уже выполнил большую часть математики и проверил, что она - атомарно - правильная. – Sorona

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