1. sbt 編碼準則

sbt 編碼準則 

此頁面討論 sbt 1.0 的編碼風格和其他準則。

一般目標 

sbt 1.0 主要以 Scala 2.12 為目標。我們將針對 Scala 2.10 進行交叉建置。

清除舊的棄用 

在 1.0 發佈之前,我們應該清除棄用。

目標為零警告 (除了棄用) 

在 Scala 2.12 上,我們的目標應該是零警告。如果交叉建置需要,則可能會出現一個例外是棄用。

文件 

在充實特性/類別實作之前,通常從 Scaladoc 開始是很有用的,它可以強迫您考慮其存在的必要性。

所有新引入的公開特性和類別,以及在較小程度上的函式和方法,都應該有 Scaladoc。許多現有的 sbt 程式碼缺乏文件,我們需要隨著時間推移來修復這種情況。如果您發現有機會新增一些文件,或改善現有文件,那麼這也會有所幫助。

套件層級文件是描述各種元件如何互動的好地方,因此請考慮在可能的情況下新增/加強它。

如需更多關於良好 Scaladoc 風格的資訊,請參閱 Scaladoc 風格指南

模組化設計 

目標小 

我們可以向建置使用者公開的方法越少,sbt 就越容易維護。

公開 API 應該針對「介面」進行編碼 

針對介面進行編碼。

隱藏實作細節 

實作細節應該隱藏在 sbt.internal.x 套件後面,其中 x 可以是主套件的名稱 (例如 io)。

較少的相互依賴性 

具有較少相依函式庫的獨立模組更容易重複使用。

隱藏外部類別 

避免在 API 中公開外部類別,除了標準的 Scala 和 Java 類別之外。

隱藏內部模組 

如果模組對公眾沒有用處,則可以宣告為內部模組。

編譯器旗標 

-encoding utf8
-deprecation
-feature
-unchecked
-Xlint
-language:higherKinds
-language:implicitConversions
-Xfuture
-Yinline-warnings
-Yno-adapted-args
-Ywarn-dead-code
-Ywarn-numeric-widen
-Ywarn-value-discard
-Xfatal-warnings

如果有無法避免的警告,則可以移除 -Xfatal-warnings

套件名稱和組織名稱 

使用附加層名稱的套件名稱,例如 IO 層的 sbt.io。發佈成品的組織名稱應保持為 org.scala-sbt

二進制彈性 

關於二進制彈性主題的良好概述是 Josh 在 2012 年的演講。此處的準則主要適用於公開的 API。

MiMa 

使用 MiMa

公開特性應僅包含 def 宣告 

  • trait 中的 valvar 會導致在子類別和人工 Foo$class.$init$ 中產生程式碼
  • lazy val 會導致在子類別中產生程式碼

抽象類別也很有用 

要特性,還是不要特性?。抽象類別不如特性靈活,但特性對二進制相容性構成更多問題。抽象類別也具有更好的 Java 互通性。

密封特性和抽象類別 

如果不需要保持類別開啟,請將其密封。

最終化葉類別 

如果不需要保持類別開啟,請將其最終化。

類型類別和子類別繼承 

具有純特性的類型類別模式可能比子類別更容易維護二進制相容性。

避免使用 case 類別,使用 sbt-datatype 

Case 類別涉及程式碼產生,這使得長期維護二進制相容性更加困難。

偏好方法多載而非預設參數值 

預設參數值實際上是程式碼產生,這使得它們難以維護。

其他公開 API 問題 

以下是關於 sbt 公開 API 的其他準則。

避免使用字串類型程式設計 

定義資料類型。

避免過度使用 def apply 

def apply 應保留給傳回類型 T 的伴生物件中的工廠方法。

使用特定的資料類型 (VectorListArray),而不是 Seq 

scala.Seqscala.collection.Seq,它是不可變的。預設為 Vector。如果需要常數前置,請使用 List。如果需要 Java 互通性,請使用 Array。請注意,在實作中完全可以使用可變集合。

避免在 Set 上呼叫 toSeq 或任何具有副作用的方法 

如果您堅持使用集合操作 (例如 containssubsetOf),則 Set 很好。通常,會明確或隱含地呼叫 toSeq,或者從 map 呼叫一些具有副作用的方法。這會將不確定性引入程式碼。

避免在 Map 上呼叫 toSeq 

與上述相同。這會引入不確定性。

如果需要 Java 互操作性,請避免在簽名中使用函式和元組 

不要使用函式和元組,而是將它們轉換為 trait。這適用於需要考慮互操作性的情況,例如實作增量編譯。

風格很重要 

使用 scalafmt 

sbt-houserules 內建 scalafmt,可一致地格式化原始碼。

避免程序語法 

明確宣告 Unit 返回值。

盡可能在 typeclass 的伴生物件中定義它們的實例 

鼓勵使用這種風格

final class FooID {}
object FooID {
  implicit val fooIdPicklerUnpicker: PicklerUnpickler[FooID] = ???
}

用於語法(豐富我的函式庫模式)的隱式轉換應該被匯入 

避免在伴生物件和 package 物件中定義隱式轉換器。

假設 IO 模組引入一個名為 RichURIURL 擴充,而 LibraryManagement 引入一個名為 GroupIDString 擴充(用於 ModuleID 語法)。這些隱式轉換應該在各自套件中名為 syntax 的物件中定義

package sbt.io

object syntax {
  implicit def uriToRichURI(uri: URI): RichURI = new RichURI(uri)
}

當所有層都可用時,sbt 套件也應該定義一個名為 syntax 的物件,它會轉發來自所有層的隱式轉換

package sbt

object syntax {
  implicit def uriToRichURI(uri: URI): io.RichURI = io.syntax.uriToRichURI(uri)
  ....
}