[英]Scala application structure
I am learning Scala now and I want to write some silly little app like a console Twitter client, or whatever. 我现在正在学习Scala,我想写一些愚蠢的小应用程序,如控制台Twitter客户端,或其他什么。 The question is, how to structure application on disk and logically.
问题是,如何在磁盘上和逻辑上构建应用程序。 I know python, and there I would just create some files with classes and then import them in the main module like
import util.ssh
or from tweets import Retweet
(strongly hoping you wouldn't mind that names, they are just for reference). 我知道python,在那里我只是创建一些带有类的文件然后在主模块中导入它们,如
import util.ssh
或者from tweets import Retweet
(强烈希望你不介意这些名字,它们仅供参考)。 But how should I do this stuff using Scala? 但是我应该如何使用Scala来做这些事情呢? Also, I have not much experience with JVM and Java, so I am a complete newbie here.
另外,我对JVM和Java没有多少经验,所以我在这里是一个完整的新手。
I'm going to disagree with Jens , here, though not all that much. 我不同意Jens ,虽然不是那么多。
My own suggestion is that you model your efforts on Maven's standard directory layout . 我自己的建议是你在Maven的标准目录布局上模拟你的工作。
Previous versions of SBT (before SBT 0.9.x) would create it automatically for you: 以前版本的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
So you'll put your source files inside myproject/src/main/scala
, for the main program, or myproject/src/test/scala
, for the tests. 因此,您将源文件放在
myproject/src/main/scala
,用于主程序,或myproject/src/test/scala
,用于测试。
Since that doesn't work anymore, there are some alternatives: 由于这不再起作用,有一些替代方案:
Install giter8 , clone ymasory's sbt.g8 template and adapt it to your necessities, and use that. 安装giter8 ,克隆ymasory的sbt.g8模板并根据您的需要进行调整,并使用它。 See below, for example, this use of unmodified ymasory's sbt.g8 template.
例如,参见下面未修改的ymasory的sbt.g8模板的使用。 I think this is one of the best alternatives to starting new projects when you have a good notion of what you want in all your projects.
我认为这是开始新项目的最佳选择之一,因为你对所有项目都有一个很好的概念。
$ 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
Use softprops's np plugin for sbt. 使用softprops的sp 插件的np插件 。 In the example below, the plugin is configured on
~/.sbt/plugins/build.sbt
, and its settings on ~/.sbt/np.sbt
, with standard sbt script. 在下面的示例中,插件在
~/.sbt/plugins/build.sbt
,其设置在~/.sbt/np.sbt
,带有标准的sbt脚本。 If you use paulp's sbt-extras, you'd need to install these things under the right Scala version subdirectory in ~/.sbt
, as it uses separate configurations for each Scala version. 如果您使用paulp的sbt-extras,则需要在
~/.sbt
的右侧Scala版本子目录下安装这些内容,因为它为每个Scala版本使用单独的配置。 In practice, this is the one I use most often. 在实践中,这是我经常使用的那个。
$ 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
You could simply create it with 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
Now, about the source layout. 现在,关于源布局。 Jens recommends following Java style.
Jens建议遵循Java风格。 Well, the Java directory layout is a requirement -- in Java.
嗯,Java目录布局是一项要求 - 在Java中。 Scala does not have the same requirement, so you have the option of following it or not.
Scala没有相同的要求,因此您可以选择是否遵循它。
If you do follow it, assuming the base package is org.dcsobral.myproject
, then source code for that package would be put inside myproject/src/main/scala/org/dcsobral/myproject/
, and so on for sub-packages. 如果你遵循它,假设基础包是
org.dcsobral.myproject
,那么该包的源代码将放在myproject/src/main/scala/org/dcsobral/myproject/
,等等用于子包。
Two common ways of diverging from that standard are: 偏离该标准的两种常见方式是:
Omitting the base package directory, and only creating subdirectories for the sub-packages. 省略基本包目录,仅创建子包的子目录。
For instance, let's say I have the packages org.dcsobral.myproject.model
, org.dcsobral.myproject.view
and org.dcsobral.myproject.controller
, then the directories would be myproject/src/main/scala/model
, myproject/src/main/scala/view
and myproject/src/main/scala/controller
. 例如,假设我有包
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
。
Putting everything together. 将所有东西放在一起。 In this case, all source files would be inside
myproject/src/main/scala
. 在这种情况下,所有源文件都在
myproject/src/main/scala
。 This is good enough for small projects. 这对小型项目来说已经足够了。 In fact, if you have no sub-projects, it is the same as above.
实际上,如果您没有子项目,则与上述相同。
And this deals with directory layout. 这涉及目录布局。
Next, let's talk about files. 接下来,我们来谈谈文件。 In Java, the practice is separating each class in its own file, whose name will follow the name of the class.
在Java中,实践是将每个类分隔在自己的文件中,该文件的名称将遵循类的名称。 This is good enough in Scala too, but you have to pay attention to some exceptions.
这在Scala中也足够好,但你必须注意一些例外。
First, Scala has object
, which Java does not have. 首先,Scala具有Java所没有的
object
。 A class
and object
of the same name are considered companions , which has some practical implications, but only if they are in the same file. 具有相同名称的
class
和object
被视为伴随 ,它具有一些实际含义,但前提是它们位于同一文件中。 So, place companion classes and objects in the same file. 因此,将伴侣类和对象放在同一个文件中。
Second, Scala has a concept known as sealed class
(or trait
), which limits subclasses (or implementing object
s) to those declared in the same file. 其次,Scala有一个称为
sealed class
(或trait
)的概念,它将子类(或实现object
)限制为在同一文件中声明的子类。 This is mostly done to create algebraic data types with pattern matching with exhaustiveness check. 这主要是为了创建具有模式匹配和穷举检查的代数数据类型。 For example:
例如:
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
If Tree
was not sealed
, then anyone could extend it, making it impossible for the compiler to know whether the match was exhaustive or not. 如果
Tree
没有sealed
,那么任何人都可以扩展它,使编译器无法知道匹配是否是详尽的。 Anyway, sealed
classes go together in the same file. 无论如何,
sealed
类一起放在同一个文件中。
Another naming convention is to name the files containing a package object
(for that package) package.scala
. 另一个命名约定是命名包含
package object
(对于该包) package.scala
。
The most basic rule is that stuff in the same package see each other. 最基本的规则是同一个包中的东西互相看见。 So, put everything in the same package, and you don't need to concern yourself with what sees what.
所以,把所有东西都放在同一个包里,你不需要关心自己看到了什么。
But Scala also have relative references and imports. 但Scala也有相对引用和导入。 This requires a bit of an explanation.
这需要一些解释。 Say I have the following declarations at the top of my file:
假设我的文件顶部有以下声明:
package org.dcsobral.myproject
package model
Everything following will be put in the package org.dcsobral.myproject.model
. 以下所有内容都将放在
org.dcsobral.myproject.model
包中。 Also, not only everything inside that package will be visible, but everything inside org.dcsobral.myproject
will be visible as well. 此外,不仅包装内的所有内容都可见,而且
org.dcsobral.myproject
内的所有org.dcsobral.myproject
也都可见。 If I just declared package org.dcsobral.myproject.model
instead, then org.dcsobral.myproject
would not be visible. 如果我只是声明了
package org.dcsobral.myproject.model
,那么org.dcsobral.myproject
将不可见。
The rule is pretty simple, but it can confuse people a bit at first. 规则很简单,但最初可能会让人感到困惑。 The reason for this rule is relative imports.
此规则的原因是相对导入。 Consider now the following statement in that file:
现在考虑该文件中的以下语句:
import view._
This import may be relative -- all imports can be relative unless you prefix it with _root_.
此导入可能是相对的 - 除非您使用
_root_.
前缀,否则所有导入都可以是相对的_root_.
. 。 It can refer to the following packages:
org.dcsobral.myproject.model.view
, org.dcsobral.myproject.view
, scala.view
and java.lang.view
. 它可以引用以下包:
org.dcsobral.myproject.model.view
, org.dcsobral.myproject.view
, scala.view
和java.lang.view
。 It could also refer to an object named view
inside scala.Predef
. 它还可以引用
scala.Predef
名为view
的对象。 Or it could be an absolute import refering to a package named view
. 或者它可以是一个绝对导入,引用一个名为
view
的包。
If more than one such package exists, it will pick one according to some precedence rules. 如果存在多个这样的包,它将根据一些优先规则选择一个。 If you needed to import something else, you can turn the import into an absolute one.
如果您需要导入其他内容,可以将导入转换为绝对导入。
This import makes everything inside the view
package (wherever it is) visible in its scope. 此导入使
view
包中的所有内容(无论它在何处)在其范围内可见。 If it happens inside a class
, and object
or a def
, then the visibility will be restricted to that. 如果它发生在
class
, object
或def
,则可见性将限制在此范围内。 It imports everything because of the ._
, which is a wildcard. 它导入所有东西,因为
._
,这是一个通配符。
An alternative might look like this: 替代方案可能如下所示:
package org.dcsobral.myproject.model
import org.dcsobral.myproject.view
import org.dcsobral.myproject.controller
In that case, the packages view
and controller
would be visible, but you'd have to name them explicitly when using them: 在这种情况下, 包
view
和controller
将是可见的,但您在使用它们时必须明确命名它们:
def post(view: view.User): Node =
Or you could use further relative imports: 或者您可以使用进一步的相对导入:
import view.User
The import
statement also enable you to rename stuff, or import everything but something. import
语句还允许您重命名内容,或导入除了某些内容之外的所有内容。 Refer to relevant documentation about it for more details. 有关详细信息,请参阅相关文档。
So, I hope this answer all your questions. 所以,我希望这能回答你所有的问题。
Scala supports and encourages the package structure of Java /JVM and pretty much the same recommendation apply: Scala支持并鼓励Java / JVM的包结构,几乎相同的建议适用:
<domain>.<module>.<layer>.<optional subpackage>
. <domain>.<module>.<layer>.<optional subpackage>
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.