Могу ли я определить класс без открытого конструктора и поместить метод фабрики для объектов этого класса в другой класс в Scala? - PullRequest
3 голосов
/ 22 октября 2010

Например (возможно, немного неуклюже с точки зрения реальной жизни, но просто для иллюстрации):

"Пользователь" - это класс case , содержащий имя пользователя и идентификатор.Идентификатор никогда не может быть установлен вручную, а экземпляр класса User без установленного идентификатора не имеет смысла.

Класс UserBase поддерживает базу пользователей и имеет метод "getUser (name: String): User", возвращающий непротиворечивого пользователяinstance.

Никто, кроме объекта UserBase, не может знать (ну кто-то может, но на самом деле не должен полагаться на это знание) идентификатор пользователя, поэтому создание экземпляра User вручную не имеет смысла (и может привести кошибки в будущем, если кто-то случайно запрограммирует это и забудет).Более того, нежелательный экземпляр пользователя, не отслеживаемый UserBase, также нежелателен.

Таким образом, задача состоит в том, чтобы сделать вызов UserBase.getUser единственным способом получить экземпляр пользователя.

Может ли это бытьреализовано в Scala?

Ответы [ 3 ]

2 голосов
/ 23 октября 2010

Вы должны поместить классы в один пакет или сделать их частью одного класса или объекта. Тогда:

object O {
  class C private[O] (val x: Int) { }
  object D { def apply(i:Int) = new C(i) }
  def getC(i:Int) = new C(i)
}

scala> O.D(5)
res0: O.C = O$C@5fa6fb3e

scala> new O.C(5)
<console>:10: error: constructor C cannot be accessed in object $iw
       new O.C(5)

scala> O.getC(5)
res1: O.C = O$C@127208e4
1 голос
/ 23 октября 2010

Вы на самом деле не проясняете, что такое «база» в этом контексте, но, учитывая ваше описание, это звучит так, будто это действительно не что иное, как фабрика для пользователей.

Обычное место, где можно поставить фабрику длякласс находится в объекте-компаньоне (это то, как это делают классы case, но техника не ограничивается просто классами case)

class User private(val id: Int, val name: String) {
  ...
}

object User {
  private def nextId() : Int = ...
  def apply(name: String) = new User(nextId(), name)
}

//now create one:
val u = User("Ivan")

Конечно, если объект Userявляется неизменным (настоятельно рекомендуется), тогда есть очень мало причин, чтобы скрыть элемент id.Вы, вероятно, также захотите, чтобы (ограниченный) метод создавал User с указанным идентификатором, в основном по причинам модульного тестирования.

Работая с такими спутниками, маловероятно, что вывсе еще нужна отдельная фабрика UserBase.Если ваша фабрика будет названа так же, как и экземпляры, которые она производит, будет получен более чистый код.

1 голос
/ 22 октября 2010

Класс case автоматически получает несколько функций, включая сопутствующий объект с методом apply () для создания экземпляров класса. Вот почему вам не нужны «новые» с кейс-классами. Если вы попытаетесь сделать явный компаньон с apply (), вы получите error: method apply is defined twice

Если вы хотите создать свой собственный метод фабрики, вам не следует использовать case-классы. Вы по-прежнему можете иметь все те же функции (toString, применять, отменять и т. Д.), Но вам придется реализовать их вручную и в соответствии со своей спецификацией.

...