Структура приложения скала
Я изучаю Scala сейчас, и я хочу написать какое-то глупое маленькое приложение, такое как консольный клиент Twitter, или что-то еще. Вопрос в том, как структурировать приложение на диске и логически. Я знаю python, и там я бы просто создал некоторые файлы с классами, а затем импортировал их в основной модуль, например import util.ssh или from tweets import Retweet (сильно надеясь, что вы не возражаете, что имена, они просто для справки). Но как должны Я делаю это с помощью Scala? Кроме того, у меня не так много опыта с JVM и Java, так что я полный новичок здесь.
2 ответов:
я не соглашусь с Йенс, здесь, хотя и не все так много.
Макет Проекта
мое собственное предложение заключается в том, что вы моделируете свои усилия на стандартный макет каталога Maven.
предыдущие версии SBT (до SBT 0.9.х) создаст его автоматически для вас:
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,
sbt.g8 шаблон и адаптировать его к вашим потребностям, и использовать это. См. ниже, например, это использование немодифицированного sbt ymasory.шаблон Г8. Я думаю, что это одна из лучших альтернатив для запуска новых проектов, когда у вас есть хорошее представление о том, что вы хотите во всех ваших проекты. $ g8 ymasory/sbt project_license_url [http://www.gnu.org/licenses/gpl-3.0.txt]: name [myproj]: project_group_id [com.example]: developer_email [[email protected]]: 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
используйте softprops плагин np для sbt. В приведенном ниже примере плагин настроен на
~/.sbt/plugins/build.sbt, и его параметры~/.sbt/np.sbt, со стандартным сценарием sbt. Если вы используете sbt-extras paulp, вам нужно будет установить эти вещи в правильном подкаталоге Scala version в~/.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 filesmkdir
вы можете просто создать его с
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 тоже, но вы должны обратите внимание на некоторые исключения.
во-первых, у Скалы есть
object, который Java не имеет. Аclassиobjectодноименными считаются спутники, что имеет некоторые практические последствия, но только если они находятся в одном файле. Итак, поместите сопутствующие классы и объекты в один файл.во-вторых, Scala имеет концепцию, известную как
sealed class(илиtrait), который ограничивает подклассы (или реализуетobjects) к тем объявлено в том же файле. Это в основном делается для создания алгебраических типов данных с сопоставлением шаблонов с проверкой полноты. Например: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.UserThe
importзаявление также позволяет переименовывать вещи или импортировать все, кроме чего-то. См. соответствующую документацию об этом для получения более подробной информации.Итак, я надеюсь, что это ответит на все ваши вопросы.
Scala поддерживает и поощряет структуру пакетов Java /JVM и в значительной степени те же рекомендации применяются:
- зеркальное отображение структуры пакета в структуре каталогов. Это не обязательно в Scala, но это помогает найти свой путь вокруг
- используйте свой обратный домен в качестве префикса пакета. Для меня это означает, что все начинается с де.шаудерхафт. Используйте то, что имеет смысл для вас, если у вас нет собственного домена
- только положить сверху классы уровня в одном файле, если они малы и тесно связаны. В противном случае придерживайтесь одного класса/объекта на файл. Исключения: сопутствующие объекты попадают в тот же файл, что и класс. Реализации запечатанного класса идут в тот же файл.
- если ваше приложение растет, вы можете иметь что-то вроде слоев и модулей и отражать их в структуре пакета, поэтому у вас может быть такая структура пакета:
<domain>.<module>.<layer>.<optional subpackage>.- не имеют циклических зависимостей от пакета, модуля или уровень слоя
Comments