Я собираюсь не согласиться с Йенсом , здесь, хотя и не так сильно.
Макет проекта
Мое собственное предложение состоит в том, чтобы вы смоделировали свои усилия на Стандартном макете каталогов Maven .
Предыдущие версии SBT (до SBT 0.9.x) создавали его автоматически для вас:
dcs@ayanami:~$ mkdir myproject
dcs@ayanami:~$ cd myproject
dcs@ayanami:~/myproject$ sbt
Project does not exist, create new project? (y/N/s) y
Name: myproject
Organization: org.dcsobral
Version [1.0]:
Scala version [2.7.7]: 2.8.1
sbt version [0.7.4]:
Getting Scala 2.7.7 ...
:: retrieving :: org.scala-tools.sbt#boot-scala
confs: [default]
2 artifacts copied, 0 already retrieved (9911kB/134ms)
Getting org.scala-tools.sbt sbt_2.7.7 0.7.4 ...
:: retrieving :: org.scala-tools.sbt#boot-app
confs: [default]
15 artifacts copied, 0 already retrieved (4096kB/91ms)
[success] Successfully initialized directory structure.
Getting Scala 2.8.1 ...
:: retrieving :: org.scala-tools.sbt#boot-scala
confs: [default]
2 artifacts copied, 0 already retrieved (15118kB/160ms)
[info] Building project myproject 1.0 against Scala 2.8.1
[info] using sbt.DefaultProject with sbt 0.7.4 and Scala 2.7.7
> quit
[info]
[info] Total session time: 8 s, completed May 6, 2011 12:31:43 PM
[success] Build completed successfully.
dcs@ayanami:~/myproject$ find . -type d -print
.
./project
./project/boot
./project/boot/scala-2.7.7
./project/boot/scala-2.7.7/lib
./project/boot/scala-2.7.7/org.scala-tools.sbt
./project/boot/scala-2.7.7/org.scala-tools.sbt/sbt
./project/boot/scala-2.7.7/org.scala-tools.sbt/sbt/0.7.4
./project/boot/scala-2.7.7/org.scala-tools.sbt/sbt/0.7.4/compiler-interface-bin_2.7.7.final
./project/boot/scala-2.7.7/org.scala-tools.sbt/sbt/0.7.4/compiler-interface-src
./project/boot/scala-2.7.7/org.scala-tools.sbt/sbt/0.7.4/compiler-interface-bin_2.8.0.RC2
./project/boot/scala-2.7.7/org.scala-tools.sbt/sbt/0.7.4/xsbti
./project/boot/scala-2.8.1
./project/boot/scala-2.8.1/lib
./target
./lib
./src
./src/main
./src/main/resources
./src/main/scala
./src/test
./src/test/resources
./src/test/scala
Таким образом, вы поместите ваши исходные файлы в myproject/src/main/scala
для основной программы или myproject/src/test/scala
для тестов.
Так как это больше не работает, есть несколько альтернатив:
giter8 и sbt.g8
Установите giter8 , клонируйте шаблон ymasory sbt.g8 и адаптируйте его под свои нужды, и используйте его. См. Ниже, например, это использование шаблона sbt.g8 неизмененного ymasory's. Я думаю, что это одна из лучших альтернатив для запуска новых проектов, когда у вас есть четкое представление о том, что вы хотите во всех своих проектах.
$ g8 ymasory/sbt
project_license_url [http://www.gnu.org/licenses/gpl-3.0.txt]:
name [myproj]:
project_group_id [com.example]:
developer_email [john.doe@example.com]:
developer_full_name [John Doe]:
project_license_name [GPLv3]:
github_username [johndoe]:
Template applied in ./myproj
$ tree myproj
myproj
├── build.sbt
├── LICENSE
├── project
│ ├── build.properties
│ ├── build.scala
│ └── plugins.sbt
├── README.md
├── sbt
└── src
└── main
└── scala
└── Main.scala
4 directories, 8 files
np plugin
Используйте плагин softprops np для sbt. В приведенном ниже примере плагин настроен на ~/.sbt/plugins/build.sbt
, а его настройки на ~/.sbt/np.sbt
со стандартным сценарием sbt. Если вы используете sbt-extras от paulp, вам нужно будет установить эти вещи в нужном подкаталоге версии Scala в ~/.sbt
, так как он использует отдельные конфигурации для каждой версии Scala. На практике это тот, который я использую чаще всего.
$ mkdir myproj; cd myproj
$ sbt 'np name:myproj org:com.example'
[info] Loading global plugins from /home/dcsobral/.sbt/plugins
[warn] Multiple resolvers having different access mechanism configured with same name 'sbt-plugin-releases'. To avoid conflict, Remove duplicate project resolvers (`resolvers`) or rename publishing resolver (`publishTo`).
[info] Set current project to default-c642a2 (in build file:/home/dcsobral/myproj/)
[info] Generated build file
[info] Generated source directories
[success] Total time: 0 s, completed Apr 12, 2013 12:08:31 PM
$ tree
.
├── build.sbt
├── src
│ ├── main
│ │ ├── resources
│ │ └── scala
│ └── test
│ ├── resources
│ └── scala
└── target
└── streams
└── compile
└── np
└── $global
└── out
12 directories, 2 files
MkDir
Вы можете просто создать его с помощью mkdir
:
$ mkdir -p myproj/src/{main,test}/{resource,scala,java}
$ tree myproj
myproj
└── src
├── main
│ ├── java
│ ├── resource
│ └── scala
└── test
├── java
├── resource
└── scala
9 directories, 0 files
Исходный макет
Теперь по поводу макета источника. Дженс рекомендует придерживаться стиля Java. Что ж, макет каталога Java - это требование - в Java. В Scala нет того же требования, поэтому вы можете выполнить его или нет.
Если вы выполните его, предполагая, что базовый пакет равен org.dcsobral.myproject
, то исходный код для этого пакета будет помещен в myproject/src/main/scala/org/dcsobral/myproject/
, и так далее для подпакетов.
Два распространенных способа отклонения от этого стандарта:
Пропуск каталога базового пакета и создание только подкаталогов для подпакетов.
Например, допустим, у меня есть пакеты org.dcsobral.myproject.model
, org.dcsobral.myproject.view
и org.dcsobral.myproject.controller
, тогда каталоги будут myproject/src/main/scala/model
, myproject/src/main/scala/view
и myproject/src/main/scala/controller
.
Собираем все вместе. В этом случае все исходные файлы будут внутри myproject/src/main/scala
. Этого достаточно для небольших проектов. Фактически, если у вас нет подпроектов, это то же самое, что и выше.
И это касается макета каталога.
Имена файлов
Далее поговорим о файлах. В Java практика отделяет каждый класс в своем собственном файле, имя которого будет следовать за именем класса. Это достаточно хорошо и в Scala, но вы должны обратить внимание на некоторые исключения.
Во-первых, у Scala есть object
, которого нет у Java. class
и object
с одинаковыми именами считаются компаньонами , что имеет некоторые практические последствия, но только , если они находятся в одном файле. Итак, поместите сопутствующие классы и объекты в один файл.
Во-вторых, в Scala есть концепция, известная как sealed class
(или trait
), которая ограничивает подклассы (или реализует object
s) теми, которые объявлены в том же файле. В основном это делается для создания алгебраических типов данных с сопоставлением с шаблоном с проверкой на полноту. Например:
sealed abstract class Tree
case class Node(left: Tree, right: Tree) extends Tree
case class Leaf(n: Int) extends Tree
scala> def isLeaf(t: Tree) = t match {
| case Leaf(n: Int) => println("Leaf "+n)
| }
<console>:11: warning: match is not exhaustive!
missing combination Node
def isLeaf(t: Tree) = t match {
^
isLeaf: (t: Tree)Unit
Если Tree
не было sealed
, тогда любой мог бы его расширить, не дав компилятору узнать, было ли совпадение исчерпывающим или нет. В любом случае, sealed
классы объединяются в одном файле.
Другое соглашение об именах - именовать файлы, содержащие package object
(для этого пакета) package.scala
.
Импорт материалов
Самое основное правило - вещи в одном пакете видят друг друга. Итак, поместите все в одну упаковку, и вам не нужно беспокоиться о том, что видит.
Но у Scala также есть относительные ссылки и импорт. Это требует немного объяснения. Скажем, у меня есть следующие объявления вверху моего файла:
package org.dcsobral.myproject
package model
Все следующее будет помещено в пакет org.dcsobral.myproject.model
.Кроме того, будет видно не только все, что находится внутри этого пакета, но и все, что находится внутри org.dcsobral.myproject
.Если бы я просто объявил package org.dcsobral.myproject.model
, тогда org.dcsobral.myproject
не было бы видно.
Правило довольно простое, но поначалу оно может немного запутать людей.Причиной этого правила является относительный импорт.Теперь рассмотрим следующее утверждение в этом файле:
import view._
Этот импорт может быть относительным - все операции импорта могут быть относительными, если вы не добавите префикс _root_.
.Это может относиться к следующим пакетам: org.dcsobral.myproject.model.view
, org.dcsobral.myproject.view
, scala.view
и java.lang.view
.Он также может ссылаться на объект с именем view
внутри scala.Predef
.Или это может быть абсолютный импорт, ссылающийся на пакет с именем view
.
Если существует более одного такого пакета, он выберет один в соответствии с некоторыми правилами приоритета.Если вам нужно было импортировать что-то еще, вы можете превратить импорт в абсолютный.
Этот импорт делает все внутри пакета view
(где бы он ни находился) видимым в своей области видимости.Если это происходит внутри class
и object
или def
, то видимость будет ограничена этим.Он импортирует все из-за ._
, который является подстановочным знаком.
Альтернатива может выглядеть следующим образом:
package org.dcsobral.myproject.model
import org.dcsobral.myproject.view
import org.dcsobral.myproject.controller
В этом случае пакетов view
и controller
будут видны, но вы должны будете назвать их явно при использовании:
def post(view: view.User): Node =
Или вы можете использовать дальнейшие относительные импорты:
import view.User
import
оператор также позволяет вам переименовывать вещи или импортировать все, кроме чего-то.Более подробную информацию см. В соответствующей документации.
Итак, я надеюсь, что это ответит на все ваши вопросы.