Swiftプロトコル向けプログラミングについて
7297 ワード
具体的なニーズから言えば
アプリケーションに複数のページ内の
しかし、異なるページには異なる
オブジェクト向けの多重化継承 オブジェクト向けの思考で問題を解決すると、
問題を解決できますが、欠点も明らかです.継承は結合をもたらしやすいです.
たとえば、
また,1つの Extension/Category
厳密にはこれは典型的なオブジェクト向けではなく、Swift/Objective-C特有の機能です.これを使用すると、クラスのインプリメンテーションファイルを変更せずに、新しいメソッドを追加できます.組合せ コンビネーションが継承より優れているのは常識で、ここでこのアニメーションを担当するクラスを定義し、
プロトコル向けの多重化方式
プロトコル拡張という特性の導入により,Swiftはプロトコル向けプログラミングという新しいプログラミングモデルをサポートした.実装する機能については、プロトコルを使用してインタフェースを定義し、プロトコル拡張を使用してデフォルトの実装を提供できます.
上のコード:
このように、
プロトコルのクラスのオブジェクトがプロトコル宣言を呼び出す方法を遵守する場合、クラス自体が実装を提供していない場合、プロトコル拡張が提供するデフォルトの実装が呼び出されます.
実際には,結合をさらに解除することもできる.この方法はlayerのみに依存し,必ずしも
また、アニメーションの時間をカスタマイズし、デフォルトの時間を指定することもできます.
コードを少し変更:
このプロトコルは、
CALayerそのものなら?簡単:
プロトコルプログラミング向けの利点は,プロトコル+拡張により1つの機能を実現し,必要な十分な必要条件を定義できることであり,多くも少なくない.これにより結合が最小限に抑えられる.利用者は積み木のようにこれらのプロトコルを任意に組み合わせてclassやstructを書いて複雑な機能を完成させることができる.実際、Swiftの標準ライブラリはほとんどeverything is starting out as a protocolです.
従来のプロトコル(例えばObjective-Cのprotocol,JavaのInterface)はインタフェースしか定義できず、実装を多重化できず、同じプロトコルの異なるクラスを遵守し、それぞれプロトコルインタフェースしか実現できず、使用シーンが制限されている.Swiftはプロトコル拡張の特性が1つ増えただけで、プログラミングモデルの進化をもたらした.
アプリケーションに複数のページ内の
UICollectionViewCell
は、同じ小さなアニメーションを実現する必要があります.選択された場合、元の0.8倍に縮小し、0.9倍に戻ります.アニメーション自体を実現するのは難しくありません: func selectWithBounce(select:Bool, animated:Bool = true){
let bounce = CAKeyframeAnimation(keyPath: "transform")
let origin = CATransform3DIdentity
let smallest = CATransform3DMakeScale(0.8, 0.8, 1)
let small = CATransform3DMakeScale(0.9, 0.9, 1)
let originValue = NSValue(CATransform3D: origin)
let smallestValue = NSValue(CATransform3D:smallest)
let smallValue = NSValue(CATransform3D:small)
if animated {
bounce.duration = 0.2
bounce.removedOnCompletion = false
if select {
bounce.values = [originValue, smallestValue, smallValue]
self.layer.addAnimation(bounce, forKey: "bounce")
}else{
bounce.values = [smallestValue, originValue]
self.layer.addAnimation(bounce, forKey: "bounce")
}
}
if select {
self.layer.transform = small
}else{
self.layer.transform = origin
}
}
しかし、異なるページには異なる
UICollectionViewCell
サブクラスがあり、どのようにしてこのアニメーションを多重化することができますか?オブジェクト向けの多重化
UICollectionViewCell
から継承されたクラスを定義することが最も考えられます.例えば、MYCollectionViewCell
と呼ばれ、このアニメーションを実現し、このアニメーションを必要とするcellがすべて継承されます.問題を解決できますが、欠点も明らかです.継承は結合をもたらしやすいです.
たとえば、
UICollectionViewCell
にもう1つの機能を追加する必要があります.この機能のインタフェースは、ABCに依存する他の3つのクラスを宣言します.Swift/Objective-Cは単に継承するしかなく,このMYCollectionViewCell
に新しい機能実装も入れれば不要な結合が導入される.このとき我々は,このアニメーション機能を用いて,MYCollectionViewCell
に依存するとともに,アニメーションとは無関係のABCの3つのクラスに依存したい.コードが硬直し始めた.UIViewController
を継承する親クラスを定義したプロジェクトもあり、多くの機能を実現し、プロジェクト内のすべてのページが継承されるように要求されています.この硬直化の欠点は明らかで,次のサブクラスコードはすべてこの親に依存しており,抽出多重化は非常に難しい.また、このカスタムUIViewController
にコードを挿入するのは本当に便利で、このクラスは機能反復に伴って徐々に膨張しやすく、メンテナンスがますます難しくなっています.また,1つの
UITableViewCell
にもこの機能が必要であれば,使用継承は実現できない.UITableViewCell
とUICollectionViewCell
はすでにUIView
の異なるサブクラスであり、UIView
を変更しない限り、同時に機能を追加することはできません.しかし、UIView
の実装ファイルは入手できません.厳密にはこれは典型的なオブジェクト向けではなく、Swift/Objective-C特有の機能です.これを使用すると、クラスのインプリメンテーションファイルを変更せずに、新しいメソッドを追加できます.
UIView
にcategoryを追加することによって、UITableViewCell
とUICollectionViewCell
が同時に機能を増加させる問題を解決することができます.欠点もExtension/Category固有で、クラスにこれを加えると、この機能が必要なくてもすべてのオブジェクトを汚染します.Objective-Cの場合、1つのファイルにimportというcategoryがない場合でもruntimeを使用してcategoryのメソッドにアクセスできます.layer
をメソッドパラメータまたはメンバー変数として渡します.欠点は、ちょっと面倒です.この小道具類は多くなったので,接着コードをたくさん書かなければならない.プロトコル向けの多重化方式
プロトコル拡張という特性の導入により,Swiftはプロトコル向けプログラミングという新しいプログラミングモデルをサポートした.実装する機能については、プロトコルを使用してインタフェースを定義し、プロトコル拡張を使用してデフォルトの実装を提供できます.
上のコード:
protocol BounceSelect {
func selectWithBounce(select:Bool, animated:Bool)
}
extension BounceSelect where Self:UIView {
func selectWithBounce(select:Bool, animated:Bool = true){
let bounce = CAKeyframeAnimation(keyPath: "transform")
let origin = CATransform3DIdentity
let smallest = CATransform3DMakeScale(0.8, 0.8, 1)
let small = CATransform3DMakeScale(0.9, 0.9, 1)
let originValue = NSValue(CATransform3D: origin)
let smallestValue = NSValue(CATransform3D:smallest)
let smallValue = NSValue(CATransform3D:small)
if animated {
bounce.duration = 0.2
bounce.removedOnCompletion = false
if select {
bounce.values = [originValue, smallestValue, smallValue]
self.layer.addAnimation(bounce, forKey: "bounce")
}else{
bounce.values = [smallestValue, originValue]
self.layer.addAnimation(bounce, forKey: "bounce")
}
}
if select {
self.layer.transform = small
}else{
self.layer.transform = origin
}
}
}
このように、
UICollectionViewCell
またはUITableViewCell
はこの機能を必要とし、このプロトコルを遵守したことを宣言するだけで、他のことは何もしなくても、func selectWithBounce(select:Bool, animated:Bool)
という方法を直接呼び出すことができます.class XYZCollectionViewCell : UICollectionViewCell, BounceSelect {
...
}
let cell: XYZCollectionViewCell
cell.selectWithBounce(true)
プロトコルのクラスのオブジェクトがプロトコル宣言を呼び出す方法を遵守する場合、クラス自体が実装を提供していない場合、プロトコル拡張が提供するデフォルトの実装が呼び出されます.
実際には,結合をさらに解除することもできる.この方法はlayerのみに依存し,必ずしも
UIView
であるとは限らず,NSView
であってもよいし,CALayer
自体であってもよい.また、アニメーションの時間をカスタマイズし、デフォルトの時間を指定することもできます.
コードを少し変更:
protocol BounceSelect {
var layer:CALayer {get}
var animationDuration:NSTimeInterval {get}
func selectWithBounce(select:Bool, animated:Bool)
}
extension BounceSelect {
var animationDuration:NSTimeInterval {
return 0.2
}
func selectWithBounce(select:Bool, animated:Bool = true){
let bounce = CAKeyframeAnimation(keyPath: "transform")
let origin = CATransform3DIdentity
let smallest = CATransform3DMakeScale(0.8, 0.8, 1)
let small = CATransform3DMakeScale(0.9, 0.9, 1)
let originValue = NSValue(CATransform3D: origin)
let smallestValue = NSValue(CATransform3D:smallest)
let smallValue = NSValue(CATransform3D:small)
if animated {
bounce.duration = animationDuration
bounce.removedOnCompletion = false
if select {
bounce.values = [originValue, smallestValue, smallValue]
self.layer.addAnimation(bounce, forKey: "bounce")
}else{
bounce.values = [smallestValue, originValue]
self.layer.addAnimation(bounce, forKey: "bounce")
}
}
if select {
self.layer.transform = small
}else{
self.layer.transform = origin
}
}
}
このプロトコルは、
var layer:CALayer {get}
によってアクセスの条件を定義し、func selectWithBounce(select:Bool, animated:Bool)
方法の宣言と実装によって、その機能を定義する.これはプラグインのようなもので、CALayerプロパティを1つ提供すれば、どのクラスもこの機能に簡単にアクセスできます.これにより、異なるクラス間でこのコードを多重化し、タイプへの依存を解除することができます.これは継承もExtension/Categoryもできないことです.CALayerそのものなら?簡単:
class XYZLayer : CALayer, BounceSelect {
//
var animationDuration:NSTimeInterval {
return 0.3
}
var layer:CALayer {
return self
}
}
プロトコルプログラミング向けの利点は,プロトコル+拡張により1つの機能を実現し,必要な十分な必要条件を定義できることであり,多くも少なくない.これにより結合が最小限に抑えられる.利用者は積み木のようにこれらのプロトコルを任意に組み合わせてclassやstructを書いて複雑な機能を完成させることができる.実際、Swiftの標準ライブラリはほとんどeverything is starting out as a protocolです.
従来のプロトコル(例えばObjective-Cのprotocol,JavaのInterface)はインタフェースしか定義できず、実装を多重化できず、同じプロトコルの異なるクラスを遵守し、それぞれプロトコルインタフェースしか実現できず、使用シーンが制限されている.Swiftはプロトコル拡張の特性が1つ増えただけで、プログラミングモデルの進化をもたらした.