Scala 应用程序结构
我现在正在学习 Scala,我想编写一些愚蠢的小应用程序,例如控制台 Twitter 客户端或其他什么。问题是,如何在磁盘上和逻辑上构造应用程序。我了解 python,在那里我只需创建一些带有类的文件,然后将它们导入主模块中,例如 import util.ssh
或 from tweets import Retweet
(强烈希望您不介意这些名称,它们仅供参考)。但是我应该如何使用 Scala 来做这些事情呢?另外,我对 JVM 和 Java 没有太多经验,所以我是一个完全的新手。
I am learning Scala now and I want to write some silly little app like a console Twitter client, or whatever. 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). But how should I do this stuff using Scala? Also, I have not much experience with JVM and Java, so I am a complete newbie here.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我不同意 Jens 的观点,尽管不是那么多。
项目布局
我自己的建议是,您在 Maven 的标准目录布局。
以前版本的 SBT(SBT 0.9.x 之前)会自动为您创建它:
因此您可以将源文件放在
myproject/src/main/scala
中作为主程序,或者myproject/src/test/scala
,用于测试。由于这不再起作用,有一些替代方案:
giter8和sbt.g8
安装giter8,克隆ymasory的sbt.g8 模板并根据您的需要进行调整,然后使用它。例如,请参见下面,使用未经修改的 ymasory 的 sbt.g8 模板。我认为,当您清楚地了解所有项目的需求时,这是开始新项目的最佳选择之一。
np 插件
使用 softprops 的 np 插件 进行 sbt。在下面的示例中,插件在
~/.sbt/plugins/build.sbt
上配置,其设置在~/.sbt/np.sbt
上,使用标准sbt 脚本。如果您使用paulp的sbt-extras,您需要将这些东西安装在~/.sbt
中正确的Scala版本子目录下,因为它为每个Scala版本使用单独的配置。在实践中,这是我最常使用的一种。mkdir
您可以简单地使用
mkdir
创建它:源布局
现在,关于源布局。 Jens 建议遵循 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 没有。同名的类
和对象
被视为同伴,这有一些实际意义,但仅如果它们位于同一个文件中。因此,将伴生类和对象放在同一个文件中。其次,Scala 有一个称为密封类(或特征)的概念,它将子类(或实现对象)限制为在相同的文件。这样做主要是为了创建具有模式匹配和详尽检查的代数数据类型。例如:
如果
Tree
不是sealed
,那么任何人都可以扩展它,从而使编译器无法知道匹配是否详尽。无论如何,密封的类都放在同一个文件中。另一种命名约定是命名包含
包对象
(对于该包)package.scala
的文件。导入东西
最基本的规则是同一包中的东西可以互相看到。因此,将所有内容放在同一个包中,您无需关心什么看到什么。
但Scala也有相对的引用和导入。这需要一些解释。假设我的文件顶部有以下声明:
以下所有内容都将放入包
org.dcsobral.myproject.model
中。此外,不仅该包内的所有内容都将可见,而且org.dcsobral.myproject
内的所有内容也将可见。如果我只是声明package org.dcsobral.myproject.model
,那么org.dcsobral.myproject
将不可见。规则非常简单,但一开始可能会让人有点困惑。这条规则的原因是相对进口。现在考虑该文件中的以下语句:
此导入可能是相对的 - 所有导入都可以是相对的,除非您使用
_root_.
作为前缀。它可以引用以下包:org.dcsobral.myproject.model.view
、org.dcsobral.myproject.view
、scala.view
和java.lang.view。它还可以引用scala.Predef
内名为view
的对象。或者它可能是引用名为view
的包的绝对导入。如果存在多个这样的包,它将根据一些优先规则选择一个。如果你需要导入其他东西,你可以将导入变成绝对导入。
此导入使
view
包内的所有内容(无论它在哪里)在其范围内可见。如果它发生在class
和object
或def
内部,那么可见性将仅限于此。由于._
(通配符),它会导入所有内容。另一种选择可能如下所示:
在这种情况下,包
view
和controller
将可见,但您必须明确命名它们使用它们时:或者您可以使用进一步的相对导入:
import
语句还允许您重命名内容,或导入除某些内容之外的所有内容。有关更多详细信息,请参阅相关文档。所以,我希望这能回答你所有的问题。
I'm going to disagree with Jens, here, though not all that much.
Project Layout
My own suggestion is that you model your efforts on Maven's standard directory layout.
Previous versions of SBT (before SBT 0.9.x) would create it automatically for you:
So you'll put your source files inside
myproject/src/main/scala
, for the main program, ormyproject/src/test/scala
, for the tests.Since that doesn't work anymore, there are some alternatives:
giter8 and sbt.g8
Install giter8, clone ymasory's sbt.g8 template and adapt it to your necessities, and use that. See below, for example, this use of unmodified ymasory's sbt.g8 template. 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.
np plugin
Use softprops's np plugin for sbt. In the example below, the plugin is configured on
~/.sbt/plugins/build.sbt
, and its settings on~/.sbt/np.sbt
, with standard sbt script. 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. In practice, this is the one I use most often.mkdir
You could simply create it with
mkdir
:Source Layout
Now, about the source layout. Jens recommends following Java style. Well, the Java directory layout is a requirement -- in Java. Scala does not have the same requirement, so you have the option of following it or not.
If you do follow it, assuming the base package is
org.dcsobral.myproject
, then source code for that package would be put insidemyproject/src/main/scala/org/dcsobral/myproject/
, and so on for sub-packages.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
andorg.dcsobral.myproject.controller
, then the directories would bemyproject/src/main/scala/model
,myproject/src/main/scala/view
andmyproject/src/main/scala/controller
.Putting everything together. In this case, all source files would be inside
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.
File Names
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. This is good enough in Scala too, but you have to pay attention to some exceptions.
First, Scala has
object
, which Java does not have. Aclass
andobject
of the same name are considered companions, which has some practical implications, but only if they are in the same file. So, place companion classes and objects in the same file.Second, Scala has a concept known as
sealed class
(ortrait
), which limits subclasses (or implementingobject
s) to those declared in the same file. This is mostly done to create algebraic data types with pattern matching with exhaustiveness check. For example:If
Tree
was notsealed
, then anyone could extend it, making it impossible for the compiler to know whether the match was exhaustive or not. Anyway,sealed
classes go together in the same file.Another naming convention is to name the files containing a
package object
(for that package)package.scala
.Importing Stuff
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. This requires a bit of an explanation. Say I have the following declarations at the top of my file:
Everything following will be put in the package
org.dcsobral.myproject.model
. Also, not only everything inside that package will be visible, but everything insideorg.dcsobral.myproject
will be visible as well. If I just declaredpackage org.dcsobral.myproject.model
instead, thenorg.dcsobral.myproject
would not be visible.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:
This import may be relative -- all imports can be relative unless you prefix it with
_root_.
. It can refer to the following packages:org.dcsobral.myproject.model.view
,org.dcsobral.myproject.view
,scala.view
andjava.lang.view
. It could also refer to an object namedview
insidescala.Predef
. Or it could be an absolute import refering to a package namedview
.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. If it happens inside aclass
, andobject
or adef
, then the visibility will be restricted to that. It imports everything because of the._
, which is a wildcard.An alternative might look like this:
In that case, the packages
view
andcontroller
would be visible, but you'd have to name them explicitly when using them:Or you could use further relative imports:
The
import
statement also enable you to rename stuff, or import everything but something. Refer to relevant documentation about it for more details.So, I hope this answer all your questions.
Scala 支持并鼓励 Java /JVM 的包结构,并且几乎适用相同的建议:
..。 <可选子包>
。Scala supports and encourages the package structure of Java /JVM and pretty much the same recommendation apply:
<domain>.<module>.<layer>.<optional subpackage>
.