19

Swift では、私は歴史的に拡張機能を使用して閉じた型を拡張し、アニメーション、数学拡張機能などの便利でロジックのない機能を提供してきました。ただし、拡張機能はコードベース全体に散在するハードな依存関係であるため、実装する前に常に 3 回考えます。拡張子としての何か。

しかし、最近、Apple が拡張機能をさらに使用することを提案しているのを目にしました。たとえば、プロトコルを個別の拡張機能として実装するなどです。

つまり、プロトコル B を実装するクラス A がある場合、次の設計になります。

class A {
    // Initializers, stored properties etc.
}

extension A: B {
    // Protocol implementation
}

そのうさぎの穴に入ると、次のような拡張機能ベースのコードが増え始めました。

fileprivate extension A {
    // Private, calculated properties
}

fileprivate extension A {
    // Private functions
}

私の一部は、プロトコルを別々の拡張機能で実装するときに得られる構成要素が好きです。これにより、クラスの個別の部分が非常に明確になります。ただし、このクラスを継承するとすぐに、拡張関数をオーバーライドできないため、この設計を変更する必要があります。

2 番目のアプローチは... 興味深いと思います。拡張機能に対して指定できるため、各プライベート プロパティに注釈を付けてプライベートとして機能させる必要がないという点が優れています。

ただし、この設計では、保存されているプロパティと保存されていないプロパティ、パブリック関数とプライベート関数も分割されているため、クラスの「ロジック」を理解するのが難しくなっています (より小さなクラスを記述します)。それは、サブクラス化の問題と相まって、私をエクステンション ワンダーランドの玄関口で少し足を止めさせます。

世界の Swift コミュニティが拡張機能をどのように見ているかを知りたいです。どう思いますか?銀の弾丸はありますか?

4

3 に答える 3

5

私は同様のアプローチを使用していますが、これは 1 文で説明できます。

タイプの責任を拡張機能に分類する

これらは、私が個々の拡張機能に入れている側面の例です:

  • クライアントから見たタイプのメイン インターフェイス。
  • プロトコルの適合性 (つまり、デリゲート プロトコル、多くの場合プライベート)。
  • シリアル化 (たとえば、NSCoding関連するすべてのもの)。
  • ネットワーク コールバックなど、バックグラウンド スレッドに存在する型の一部。

場合によっては、単一のアスペクトの複雑さが増すと、型の実装を複数のファイルに分割することさえあります。

実装関連のコードをどのようにソートするかを説明する詳細を次に示します。

  • 焦点は機能メンバーシップにあります。
  • パブリック実装とプライベート実装を近くに保ちますが、分離してください。
  • varと の間で分割しないでくださいfunc
  • ネストされた型、イニシャライザ、プロトコル準拠など、機能の実装のすべての側面をまとめます。

アドバンテージ

型の側面を分離する主な理由は、読みやすく理解しやすくするためです。

外国の (または自分の古い) コードを読むとき、全体像を理解することは、多くの場合、飛び込んでいく上で最も難しい部分です。あるメソッドのコンテキストのアイデアを開発者に与えることは、非常に役立ちます。

もう 1 つの利点があります。アクセス制御により、誤って何かを呼び出さないようにすることが容易になります。バックグラウンド スレッドからのみ呼び出されることになっているメソッドはprivate、「バックグラウンド」拡張機能で宣言できます。現在、他の場所から呼び出すことはできません。

現在の制限

Swift 3 では、このスタイルに特定の制限が課されています。メインの型の実装にしか存在できないものがいくつかあります。

  • 保存されたプロパティ
  • func/var のオーバーライド
  • オーバーライド可能な func/var
  • 必要な (指定された) 初期化子

これらの制限 (少なくとも最初の 3 つ) は、オブジェクトのデータ レイアウト (および純粋な Swift の監視テーブル) を事前に知る必要があるためです。拡張機能は実行時に (フレームワーク、プラグイン、dlopen などを介して) 遅くロードされる可能性があり、インスタンスが作成された後に型のレイアウトを変更すると、ABI が壊れます。

Swift チームへの控えめな提案 :)

1 つのモジュールのすべてのコードは、同時に使用できることが保証されています。Swift コンパイラが1 つのモジュール内で型を「構成」することを許可する場合、機能面を完全に分離することを妨げる制限を回避できます。型の合成とは、コンパイラが、型のレイアウトを定義するすべての宣言をモジュール内のすべてのファイルから収集することを意味します。言語の他の側面と同様に、ファイル内の依存関係が自動的に検出されます。

これにより、「アスペクト指向」の拡張機能を実際に作成できるようになります。メイン宣言で格納されたプロパティまたはオーバーライドを宣言する必要がないため、アクセス制御と関心の分離が向上します。

于 2016-11-09T15:33:38.203 に答える