※以下はA Warm Welcome to Structs and Value Types(objc.io, Issue 16: Swift · September 2014)の 日本語訳です。
著者:By Andy Matuschak
対象
Swiftで構造体とクラスの使い分けがしっくりしていない方。
A Warm Welcome to Structs and Value Types(objc.io #16-2日本語訳)
Objective-C -そして Ruby、PythonまたはJavaScriptなどの言語 -にこだわっている場合、Swiftの構造体が卓越していことは、特に別なものに見えるかもしれません。クラスは、オブジェクト指向言語における構造の伝統的な単位です。実際に、構造体とは対照的に、Swiftクラスは実装の継承、(限定された)反射、deinitializers、複数の所有者をサポートしています。
クラスが構造体より強力であるならば、なぜ構造体を使うのか?まあ、それはとても柔軟なブロックにする限定されたスコープが、確かにあるからです。この記事で、構造体とその他の値型が、コードの透明性、柔軟性および信頼性を根本的に改善できる方法を学びます。
Value Types and Reference Types (値型と参照型)
振る舞いにおける小さな違いは、構造上の可能性に至る。そしてこちら:構造体は値型であり、クラスは参照型です。
値型のインスタンスは、アサインしたり、関数の引数として使われる所では、どこでもコピーされる。数値、文字列、配列、辞書、列挙型、タプル、および構造体は値型です。例えば以下。
var a = "Hello"
var b = a
b.extend(", world")
println("a: \(a); b: \(b)") // a: Hello; b: Hello, world
参照型(主に:クラス、関数)のインスタンスは、複数の所有者を持つことができます。新しい変数に参照を代入するまたは関数に渡すとき、それらの位置はすべて同じインスタンスを指す。
これはオブジェクトで慣れている振る舞いです。例えば:
var a = UIView()
var b = a
b.alpha = 0.5
println("a: \(a.alpha); b: \(b.alpha)") // a: 0.5; b: 0.5
これらの二つのカテゴリーの違いは、小さいようだが、値型と参照型の選択が、システムのアーキテクチャに大きな結果を与え得る。
Building Our Intuition(直感の構築)
値型と参照型がどのように振る舞うのかの違いを理解したので、それらを使用する方法の違いについて話しましょう。この議論で、典型的な参照型としてのオブジェクトに焦点を合わせます。コードの中で、我々は現実の世界でオブジェクトを参照するのと同じ方法で対象を参照します。
本は、しばしばオブジェクト指向プログラミングを教えるために、実世界のメタファーを使用します。Dog
クラスを作り、そしてfido
を定義してインスタンス化できます。システムの異なる部分にfido
を渡す場合、それらはすべて同じfido
について話している。それは、理に適っている。もしあなたが現実にFidoという名の犬を飼っていたら、会話でその犬について話すときはいつでも それが意味するものは何でも、— 犬そのものではなく、犬の名前を渡している。あなたは、Fidoが誰かという考えも持つ誰かに依存している。オブジェクトを使用すると、システムにインスタンスの「名前」を渡しています。
値は、データのようなものです。あなたが誰かに経費の一覧を送信する場合、あなたはその人に、その情報を表すラベルを送信していません。— 情報そのものを送信しています。受け取り手は、誰にも相談せずに、合計を計算し、以降相談する費用を書き留めることができます。受け取り手が、経費をプリントアウトし、それらを変更しても、あなたがまだ持っている一覧は、変更されていない。
説明のように— 値は、おそらく価格を表す数値、または文字列を指定できます。それは、オプションの中からの選択ーこの出費は、夕食のために、旅行のために、または、材料のためにでしたか?というような列挙型:であるかもしれません。
これは、緯度と経度を指定するCLLocationCoordinate2D構造体のように、位置と名付けれた他のいくつかの値が含まれている可能性があります。あるいは、他の値の一覧等など...であり得ます。
Fidoは走り回り、自分の意のもと吠えます。彼は、他のどのような犬と違う特定の行動をするかもしれません。彼は、他と確立された関係があるかもしれません。あなたは、Fidoを他の犬と交換できません。 — あなたの子供は、違いを見分けることができます!しかし、経費の表では、孤立して存在します。これらの文字列と数字は、何もしません。それらは、あなたのもとから変わらないでしょう。"6"を最初のカラムにどんな方法で書こうとも、それは"6"のままである。
そして、それは値型についてとても素晴らしいことです。
The Advantages of Value Types(値型のアドバンテージ)
Objective-CとCは、値型を持っていましたが、Swiftは、前もって実行不可能なシナリオで、それらを使用することができます。例えば、ジェネリックシステムは、handle値と参照型を交換できるような抽象化を可能にします:Array
は、UIView
に対してと同様にInt
に対しても、等しく働く。列挙型は、 Swiftにおいて、非常に、より表現に富みます。なぜならそれらは、値を運ぶこととメソッドを明記できるからです。構造体は、プロトコルに準拠し、メソッドを明記することができます。
Swiftの値型に対して、拡張されたサポートは、大きな機会を与える。:値型は、あなたのコードを簡単にする非常に柔軟なツールです。肥満したクラスから孤立した、予測可能なコンポーネントを抽出するために、それらを使うことができます。値型は、デフォルトで透明性を生成するために一緒に働く多くの特性を強要— 少なくとも奨励 —します。
このセクションでは、値型を勧める特性のいくつかを説明します。これらの特性を持つオブジェクトを作れることは、注目に値する。しかし、言語はそうすることにプレッシャーを与えない。何かしらのコードでオブジェクトを見たら、これらの特性の合理的な予想を持たないでしょう。一方、値型を見たら、合理的な期待を持つでしょう。値型がこれらの特性を持つだけでなく、理に適った一般化であることは真実である。すべての値型には、これらの特性があるというわけではないというのは本当です— 我々は、まもなくそれをカバーします — しかし、これらは理にかなった一般化です。
Value Types Want to Be Inert(値型は自動力のない状態である)
値型は、一般的には、動作しません。一般的には不活性です。データを格納し、そのデータを使って、計算を実行するメソッドを公開します。これらの方法のいくつかは、値型は、それ自体を変異させる可能性があります。しかし、制御フローは、厳密に単一インスタンスの所有者によって制御されます。
そして、これは素晴らしいことです!単一のオーナーにより起動され、実行するコードに関して推論する方が、はるかに簡単です。
対照的にオブジェクトはタイマーのターゲットとして、それ自身を登録することがあります。システムからイベントを受け取ることがあります。この種の相互作用は、参照型が複数所有者の意味論を必要とする。なぜならば、値型は、単一の所有者のみを持つことが可能で、deinitializersが無いので、それ自身で副作用を実行する値型を書きにくいからです。
Value Types Want to Be Isolated(値型は孤立している)
一般的な値型は、外部コンポーネントの動作に対して、暗黙的依存関係はありません。単一の所有者である相互作用であることは、知ることのできない数の所有者との参照型の相互作用より、一目でとても理解しやすいです。それは、孤立しています。
あなたが、可変インスタンスへの参照にアクセスしている場合は、あなたはそのすべての他の所有者に暗黙の依存関係があります。:それらは、いつでもあなたのもとから、そのインスタンスを変更することができます。
Value Types Want to Be Interchangeable(値型は交換可能)
値型は、新しい変数に割り当てられるたびにコピーされるので、それらコピーの全てが完全に交換可能です。
渡された値を安全に保持し、そして後で新しいものとしてその値を使うことができる。誰もそれを含むデータを使うことの他を何でも使っても他と比較できない。交換可能性は、どのように与えられた値が構築されたかは問題ではないことを意味しー==
で等しいものである限り、すべての用途で同等である。
システム内でコンポーネント間で通信する値型を使用してるならば、あなたは容易にコンポーネントのグラフの周りにシフトすることができます。タッチサンプルのシーケンスを描画するviewを持っていますか?タッチサンプルのシーケンスを消費し、ユーザの指が前のサンプルに基づいて移動する場所の推定値を追加し、新しいシーケンスを返すコンポーネントを作ることによって、viewのコードに触れることなく、タッチの待ち時間を補償できます。そのviewに対して、自信を持って新しいコンポーネントの出力を与えることができます。-違いを告げることはできない。
値型を扱うユニットテストを書く空想的なモックフレームワークが、必要ではありません。アプリを通して、インスタンスから値を区別のつかない状態で’直接’構築できます。上記のタッチ予測コンポーネントは、ユニットテストが簡単です。:予測可能な値型が入り、予測かのな値型が出ます。副作用がありません。
これは、大きなアドバンテージです。伝統的な振舞いオブジェクトのアーキテクチャーではそのオブジェクトとシステムの残りの部分との相互作用をテストする必要があります。それは、一般的に厄介なモックをつくることや、それらの関係を確立する広範囲にわたるコードのセットアップを意味する。値型は、孤立、不活性、および互換であるので、値を直接構築し、メソッドを呼び、出力を調べることができます。より広範囲で、簡単なテストは、変更が容易なコードを意味する。
Not All Value Types Have These Properties
値型の構造体は、これらのプロパティを促進する一方、それらに違反する値型を確かにつくることができる。
所有者から呼ばれないで実行されるコードを含む値型は、多くの場合予測不可能で、一般的に避けるべきです。例えば、構造体のイニシャライザは、いくつかの作業をスケジューリンングするためにdispach_after
を呼ぶかもしれません。しかし、この関数にこの構造体のインスタンスを渡すと、コピーが作られるので、スケジュールされた効果が重複してしまいます。値型は不活性であるべきです。
参照を含む値型は、必ずしも隔離されておらず、一般的には避けるべきである。:それらは、その参照先のすべての所有者への依存性を帯びています。外部参照は、幾分か複雑な方法でシステムの残りにつながっているかもしれないので、これらの値型もまた容易に値を交換できない。
The Object of Objects(オブジェクトのオブジェクト)
私は、断固として不活性な値無しで、すべてのものを構築することを示唆しているわけではありません。
オブジェクトは、上記の特性を持たないからこそ便利です。オブジェクトはシステム内で動作するエンティティです。これは、同一性を持ちます。多くの場合、独立して振舞うことができます。
その振る舞いは、しばしば複雑で、推論することは困難です。しかし、詳細の幾つかは通常単純な値とそれらの値を含む単離された関数によって、表すことができる。これらの詳細は、オブジェクトの複雑な振る舞いでもつれている必要はない。それらを分離することにより、オブジェクトの振る舞いは、それ自体明確になります。
オブジェクトを予測可能な、純粋な値レイヤー上の薄い、命令レイヤーと考えてください。
オブジェクトは、値によって定義された状態を維持するが、それらの値が考慮され、独立したオブジェクトの操作することができます。その値レイヤーは、本当に状態を持っていません。それだけでデータを表し、変える。そのデータは、値が用いられているコンテキストに応じ、データは、状態として、より高いレベルの意味を持っていたり、持っていない可能性があります。
オブジェクトは、I/Oやネットワークのような副作用する。しかし、これらの副作用を最終的に駆動するデータ、計算、および自明でない決定は、値レイヤーにすべて存在します。オブジェクトは、副作用の不純な領域に、それらの純粋で予測可能な結果を伝える膜のようなものです。
オブジェクトは、他のオブジェクトとコミュニケートすることができる。しかし、外部の命令レイヤーで永続的な接続を生成するつもりである場合を除きオブジェクトは、一般的に値を送る。参照ではない。
A Summarizing Pitch for Value Types(値タイプの要約ピッチ)
値型は、典型的なアーキテクチャを著しく明確、単純でよりテスト可能にします。
値型は一般的に外部の状態に、殆どあるいは全く依存性が無いので、それらについて推論する際、考慮する必要は殆ど無い。
値型は本質的に生成でき、再利用可能である。なぜなら、値型は交換可能であるからです。
最終的に値レイヤーは、アプリケーションの不活性なビジネスロジックから能動的な振る舞い要素を分離することができます。より多くのコードを不活性にするのでシステムは、よりテストしやすくなり、時間の経過とともに変更することが容易になります。
References
Boundaries,by Gary Bernhardt, 同様の2レベルアーキテクチャを提案し、同時実行性とテストのための利点について詳しく説明。
Are We There Yet?, by Rich Hickey, 値、状態、およびアイデンティティの区別について詳しく説明。
The Structure and Interpretation of Computer Programs, by Hal Abelson and Gerald Sussman, 単純な値でどれだけ表すことができるかを説明。