Javaのバックグラウンドから来て、私は常にインスタンス変数をプライベートとしてマークします。私はscalaを学んでおり、val / varインスタンスを表示したほとんどすべてのコードにデフォルト(パブリック)アクセスがあります。なぜこれがアクセスなのですか?情報隠蔽/カプセル化の原則を破りませんか?
4 に答える
どのコードを指定しているかを教えていただけると助かりますが、一部のコード例は簡略化されており、その例で示されていることを強調していることに注意してください。デフォルトのアクセスは public であるため、簡単にするために修飾子を省略してしまうことがよくあります。
とはいえ、 aは不変であるため、これがクラスの API の一部であることを認識している限り、val
公開したままにしても大きな害はありません。それは完全に大丈夫です:
class DataThingy(data: Array[Double) {
val sum = data.sum
}
または、公開してはならない実装の詳細である可能性があります。
class Statistics(data: Array[Double]) {
val sum = data.sum
val sumOfSquares = data.map(x => x*x).sum
val expectationSquared = (sum * sum)/(data.length*data.length)
val expectationOfSquare = sumOfSquares/data.length
val varianceOfSample = expectationOfSquare - expectationSquared
val standardDeviation = math.sqrt(data.length*varianceOfSample/(data.length-1))
}
ここでは、標準偏差を計算するためのすべての中間ステップをクラスに散らかしています。これは、浮動小数点数で標準偏差を計算する最も数値的に安定した方法ではないことを考えると、特にばかげています。
これらすべてを単に非公開にするよりも、可能であればローカル ブロックまたはprivate[this]
定義を使用して中間計算を実行する方が適切です。
val sum = data.sum
val standardDeviation = {
val sumOfSquares = ...
...
math.sqrt(...)
}
また
val sum = data.sum
private[this] def findSdFromSquares(s: Double, ssq: Double) = { ... }
val standardDeviation = findMySD(sum, data.map(x => x*x).sum)
後で使用するために計算を保存する必要がある場合は、private val
またはprivate[this] val
が適していますが、計算の中間ステップにすぎない場合は、上記のオプションの方が適しています。
同様にvar
、インターフェイスの一部である a を公開しても害はありません。たとえば、変更可能なベクトルのベクトル座標です。ただし、それが実装の詳細である場合は、それらを作成する必要がありますprivate
(できれば:private[this]
できれば!)。
Java と Scala の重要な違いの 1 つは、Java では、ソースとバイナリの互換性を損なうことなく、パブリック変数を getter および setter メソッド (またはその逆) に置き換えることができないことです。Scala ではそれが可能です。
したがって、Java では、パブリック変数がある場合、それが変数であるという事実がユーザーに公開され、それを変更した場合、ユーザーは自分のコードを変更する必要があります。var
Scala では、パブリックを getter メソッドと setter メソッド (または publicを getter メソッドのみ) に置き換えることができval
、ユーザーは違いを知る必要がありません。その意味で、実装の詳細は公開されません。
例として、長方形クラスを考えてみましょう:
class Rectangle(val width: Int, val height:Int) {
val area = width * height
}
後で、領域を変数として格納するのではなく、呼び出されるたびに計算する必要があると判断した場合はどうなるでしょうか。
Java では、状況は次のようになります。getter メソッドとプライベート変数を使用した場合、変数を削除して、変数を使用する代わりに getter メソッドを変更して面積を計算することができます。ユーザー コードを変更する必要はありません。しかし、パブリック変数を使用したため、ユーザー コードを壊さざるを得なくなりました :-(
Scala では違います。 val
toを変更するだけdef
で、それだけです。ユーザー コードを変更する必要はありません。
実際、一部の Scala 開発者は、デフォルト アクセスを使いすぎる傾向があります。ただし、有名な Scala プロジェクト (たとえば、Twitter の Finagle) で適切な例を見つけることができます。
一方、不変値としてオブジェクトを作成することは、Scala の標準的な方法です。完全に不変であれば、すべての属性を非表示にする必要はありません。
もう少し一般的なアプローチで質問に答えたいと思います。あなたが探している答えは、Scala が構築されている設計パラダイムに関係していると思います。Java で見られるような従来の手続き型/オブジェクト指向のアプローチの代わりに、関数型プログラミングがより高度な拡張に使用されます。もちろん、あなたが言及したすべてのコードをカバーすることはできませんが、一般的に (よく書かれた) Scala コードは多くの可変性を必要としません。
Rex が指摘したval
ように、 は不変であるため、公開しない理由はほとんどありません。しかし、私が見るところ、不変性はそれ自体が目標ではなく、関数型プログラミングの結果です。したがって、機能をパーツのようなものと見なすと、ブラック ボックスのようなものx -> function -> y
になります。function
正しく動作する限り、それが何をするかはあまり気にしません。Haskell Wikiが書いているように:
純粋に関数型のプログラムは通常、不変データで動作します。既存の値を変更する代わりに、変更されたコピーが作成され、元の値が保持されます。
これはクロージャの欠落の説明にもなります。なぜなら、私たちが伝統的に隠したかった部分は関数で実行され、とにかく隠されているからです。
つまり、簡潔に言うと、Scala では可変性と閉鎖がより冗長になっていると主張したいと思います。そして、回避できるのに、なぜゲッターとセッターで物事を混乱させるのでしょうか?