61 votes

Scala structure de l'application

Je suis en train d'apprendre Scala maintenant et j'ai envie d'écrire quelques silly little app comme une console client Twitter, ou quoi que ce soit. La question est de savoir comment la structure de l'application sur le disque, et logiquement. Je sais python, et là, je voudrais juste créer des fichiers avec les classes et les importer dans le module principal, comme import util.ssh ou from tweets import Retweet (fortement en espérant que vous ne me dérangerait pas de noms, ils sont seulement pour la référence). Mais comment doit - je faire ce genre de choses à l'aide de la Scala? Aussi, je n'ai pas beaucoup d'expérience avec la JVM et Java, donc je suis un débutant complet ici.

112voto

Daniel C. Sobral Points 159554

Je ne suis pas daccord avec Jens, ici, mais pas tant que ça.

Présentation De Projet

Ma propre suggestion est que, vous modèle de vos efforts sur Maven du répertoire standard de mise en page.

Les versions précédentes de SBT (avant SBT 0.9.x) serait de créer automatiquement pour vous:

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

Donc, vous allez mettre vos fichiers source à l'intérieur d' myproject/src/main/scala, pour le programme principal, ou myproject/src/test/scala, pour les tests.

Depuis ne fonctionne plus, il y a des alternatives:

giter8 et sbt.g8

Installer giter8, clone ymasory du sbt.g8 modèle et de l'adapter à vos nécessités, et de l'utiliser. Voir ci-dessous, par exemple, cette utilisation de la non modifié ymasory du sbt.g8 modèle. Je pense que c'est l'une des meilleures alternatives pour le lancement de nouveaux projets lorsque vous avez une bonne notion de ce que vous voulez dans tous vos projets.

$ 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

Utilisation softprops du np plugin pour sbt. Dans l'exemple ci-dessous, le plugin est configuré sur ~/.sbt/plugins/build.sbt, et ses paramètres sur ~/.sbt/np.sbt, avec des normes sbt script. Si vous utilisez paulp du sbt-les extras, vous devez installer ces choses dans le cadre du droit de la Scala sous-répertoire de version en ~/.sbt, car il utilise une configuration distincte pour chaque Scala version. Dans la pratique, c'est celui que j'utilise le plus souvent.

$ 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

Vous pouvez créer simplement il avec 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

Mise En Page Source

Maintenant, à propos de la mise en page source. Jens recommande Java suivant le style. Eh bien, la Java la disposition des répertoires est une exigence -- en Java. Scala n'a pas la même exigence, de sorte que vous avez le choix de suivre ou non.

Si vous ne le suivez, en supposant que le forfait de base est de org.dcsobral.myproject, puis le code source de ce package d'être mis à l'intérieur d' myproject/src/main/scala/org/dcsobral/myproject/, et ainsi de suite pour les sous-packages.

Deux façons de s'écarter de cette norme sont:

  • L'omission de la base du répertoire du package, et seulement de créer des sous-répertoires pour les sous-packages.

    Par exemple, disons que j'ai les packages org.dcsobral.myproject.model, org.dcsobral.myproject.view et org.dcsobral.myproject.controller, puis les répertoires serait myproject/src/main/scala/model, myproject/src/main/scala/view et myproject/src/main/scala/controller.

  • Mettre le tout ensemble. Dans ce cas, tous les fichiers source serait à l'intérieur d' myproject/src/main/scala. C'est assez bon pour les petits projets. En fait, si vous n'avez pas de sous-projets, c'est la même que ci-dessus.

Et il traite de la disposition des répertoires.

Les Noms De Fichier

Ensuite, nous allons parler de fichiers. En Java, la pratique est de séparer chaque classe dans son propre fichier, dont le nom va suivre le nom de la classe. Ce est assez bon dans la Scala trop, mais vous devez faire attention à quelques exceptions près.

Tout d'abord, Scala a object, Java n'a pas. Un class et object du même nom sont considérés comme des compagnons, qui a certaines conséquences pratiques, mais seulement si elles sont dans le même fichier. Donc, place compagnon de classes et d'objets dans le même fichier.

Deuxièmement, Scala est un concept connu sous le nom sealed class (ou trait), ce qui limite les sous-classes (ou de la mise en œuvre de objects) à ceux déclarés dans le même fichier. C'est surtout fait pour créer des types de données algébriques avec le modèle correspondant à vérifier l'exhaustivité. Par exemple:

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

Si Tree n'a pas été sealed, alors n'importe qui pourrait s'étendre, ce qui rend impossible pour le compilateur de savoir si le match est exhaustive ou non. De toute façon, sealed classes d'aller ensemble dans le même fichier.

Une autre convention d'affectation des noms pour nommer les fichiers contenant un package object (pour que le paquet) package.scala.

L'Importation De Trucs

La plupart règle de base est que des trucs dans le même package de voir les uns les autres. Donc, de tout mettre dans le même paquet, et vous n'avez pas besoin de vous soucier de ce que voit quoi.

Mais Scala ont également des références relatives et des importations. Cela nécessite un peu d'explication. Dire que j'ai les déclarations suivantes en haut de mon fichier:

package org.dcsobral.myproject
package model

Tout ce qui suit sera mis dans le paquet org.dcsobral.myproject.model. Aussi, non seulement tout à l'intérieur de ce paquet sera visible, mais à l'intérieur tout org.dcsobral.myproject sera visible. Si je viens a déclaré package org.dcsobral.myproject.model au lieu de cela, alors org.dcsobral.myproject ne serait pas visible.

La règle est assez simple, mais il peut confondre les gens un peu au premier abord. La raison de cette règle est relative des importations. Considérons maintenant la déclaration suivante dans ce fichier:

import view._

Cette importation peut être relative-toutes les importations ne peut être que relative, à moins que vous préfixe avec _root_.. Il peut se référer aux paquets suivants: org.dcsobral.myproject.model.view, org.dcsobral.myproject.view, scala.view et java.lang.view. Il pourrait également se référer à un objet nommé view à l'intérieur d' scala.Predef. Ou il pourrait être un absolu à l'importation, qui fait référence à un package nommé view.

Si plus d'un tel programme existe, il va choisir un en fonction de certaines règles de priorité. Si vous avez besoin d'importer quelque chose d'autre, vous pouvez tourner à l'importation dans l'absolu.

Cette importation rend tout à l'intérieur de l' view package (où il est) visible dans son champ d'application. Si cela se produit à l'intérieur d'un class, et object ou def, alors que la visibilité sera limité à cela. Il importe tout à cause de l' ._, ce qui est un caractère générique.

Une alternative pourrait ressembler à ceci:

package org.dcsobral.myproject.model
import org.dcsobral.myproject.view
import org.dcsobral.myproject.controller

Dans ce cas, les paquets view et controller soit visible, mais vous auriez à les nommer explicitement lors de leur utilisation:

def post(view: view.User): Node =

Ou vous pouvez utiliser d'autres relative des importations:

import view.User

L' import déclaration aussi vous permettre de renommer des trucs, ou l'importation de tout, mais quelque chose. Reportez-vous à la documentation pertinente à ce sujet pour plus de détails.

Donc, j'espère que cette réponse à toutes vos questions.

13voto

Jens Schauder Points 23468

Scala soutient et encourage la structure du package Java /JVM et à peu près la même recommandation s'appliquent:

  • miroir de la structure du package dans la structure de répertoire. Ce n'est pas nécessaire à la Scala, mais il aide à trouver votre chemin autour de
  • utilisez votre inverse de domaine comme un préfixe de paquet. Pour moi, cela signifie tout commence avec de.schauderhaft. Utiliser quelque chose qui fait sens pour vous, si vous n'avez pas vous propre nom de domaine
  • seulement mettre de haut niveau des classes dans un fichier si elles sont de petite taille et sont étroitement liés. Sinon bâton avec une classe/objet par fichier. Exceptions: compagnon objets sont dans le même fichier que celui de la classe. Les implémentations d'une classe scellée aller dans le même fichier.
  • si vous app grandit, vous devriez avoir quelque chose comme des couches et des modules de miroir et de ceux de la structure de paquet, de sorte que vous pourriez avoir un paquet de structure comme ceci: <domain>.<module>.<layer>.<optional subpackage>.
  • n'ont pas cyclique dépendances sur un paquet, ou module de la couche de niveau

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X