Scala应用程序结构

标签 scala

我现在正在学习 Scala,我想编写一些愚蠢的小应用程序,比如控制台 Twitter 客户端,或者其他什么。问题是,如何在磁盘上和逻辑上构建应用程序。我知道 python,在那里我会创建一些带有类的文件,然后将它们导入到主模块中,例如 import util.sshfrom tweets import Retweet (强烈希望你不会介意这些名字,它们仅供引用)。但是如何应该我用 Scala 做这些东西?另外,我对 JVM 和 Java 没有太多经验,所以我在这里完全是新手。

最佳答案

我将不同意 Jens ,在这里,虽然不是那么多。

项目布局

我自己的建议是,您可以在 Maven's standard directory layout 上模拟您的努力。 .

以前版本的 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模板并使其适应您的需要,然后使用它。例如,请参见下文,使用未修改的 ymasory 的 sbt.g8 模板。我认为当您对所有项目中想要的内容有一个很好的概念时,这是启动新项目的最佳选择之一。
$ 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插件

使用软 Prop 的 np plugin对于 sbt。在下面的示例中,插件在 ~/.sbt/plugins/build.sbt 上配置,以及它在 ~/.sbt/np.sbt 上的设置, 使用标准的 sbt 脚本。如果您使用 paulp 的 sbt-extras,您需要将这些东西安装在 ~/.sbt 中正确的 Scala 版本子目录下。 ,因为它为每个 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 -p myproj/src/{main,test}/{resource,scala,java}
$ tree myproj
myproj
└── src
    ├── main
    │   ├── java
    │   ├── resource
    │   └── scala
    └── test
        ├── java
        ├── resource
        └── scala

9 directories, 0 files

源布局

现在,关于源布局。 Jens 推荐遵循 Java 风格。嗯,Java 目录布局是必需的——在 Java 中。 Scala 没有相同的要求,因此您可以选择是否遵循它。

如果你遵循它,假设基础包是 org.dcsobral.myproject ,然后该包的源代码将放在 myproject/src/main/scala/org/dcsobral/myproject/ 中,等子包。

偏离该标准的两种常见方式是:
  • 省略基本包目录,只为子包创建子目录。

    例如,假设我有包 org.dcsobral.myproject.model , org.dcsobral.myproject.vieworg.dcsobral.myproject.controller ,那么目录将是 myproject/src/main/scala/model , myproject/src/main/scala/viewmyproject/src/main/scala/controller .
  • 把所有东西放在一起。在这种情况下,所有源文件都在 myproject/src/main/scala 中。 .这对于小型项目来说已经足够了。其实,如果你没有子项目,就和上面一样。

  • 这涉及目录布局。

    文件名

    接下来,让我们谈谈文件。在 Java 中,实践是将每个类分隔在其自己的文件中,其名称将跟随类的名称。这在 Scala 中也足够好,但您必须注意一些异常(exception)情况。

    首先,Scala有object ,这是Java没有的。一个 classobject同名被认为是同伴,这有一些实际意义,但前提是它们在同一个文件中。因此,将伴随类和对象放在同一个文件中。

    其次,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.viewjava.lang.view .它还可以引用名为 view 的对象。内scala.Predef .或者它可能是引用名为 view 的包的绝对导入.

    如果存在多个这样的包,它会根据一些优先规则选择一个。如果您需要导入其他内容,您可以将导入变成绝对导入。

    此导入使 view 中的所有内容都在包(无论它在哪里)在其范围内可见。如果它发生在 class 内, 和 objectdef ,那么可见性将仅限于此。由于 ._,它导入了所有内容,这是一个通配符。

    另一种可能如下所示:
    package org.dcsobral.myproject.model
    import org.dcsobral.myproject.view
    import org.dcsobral.myproject.controller
    

    在这种情况下,包 viewcontroller将是可见的,但您必须在使用它们时明确命名它们:
    def post(view: view.User): Node =
    

    或者您可以使用进一步的相对导入:
    import view.User
    
    import语句还使您能够重命名内容,或导入除某些内容之外的所有内容。有关更多详细信息,请参阅有关它的相关文档。

    所以,我希望这能回答你所有的问题。

    关于Scala应用程序结构,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5910791/

    相关文章:

    java - Scala 中 POJO 的模式匹配?

    scala - 无法从 akka 中找到 Await

    Scala (Play 2.4.x) 如何使用@inject() 注解调用类

    scala - 将HDFS文件读取为字符串的最佳方法?

    Scala Function.tupled 与 f.tupled

    scala - Scala 中的嵌套默认映射

    scala - Spark 将自定义架构应用于 DataFrame

    string - Scala.Predef.String 类型的表达式不符合预期的 String 类型

    java - 关于 Intellij IDEA 与 scala 的问题

    scala - 避免递归 MTL 类上的隐式扩展发散