概要
呼び出すメソッドは同じでも、オブジェクトの型によって振る舞いを変化させることをポリモーフィズム(多態性)と呼びます。本記事ではProtocolを使ったポリモーフィズムの実装例を紹介します。
背景
そもそも何故ポリモーフィズムを使う必要があるのでしょうか。ポリモーフィズムのメリットとして、「メソッドの呼び出し元でオブジェクトの具体的な型を知らなくても済む」という点が挙げられます。例えば、似たような性質を持つが厳密には異なる二つのクラス、クラスAとクラスBがあるとします。この二つのクラスは共に同名のメソッド(doSomethingとします)を持っていますが、その中の処理は異なるものとします。
もしdoSomethingの呼び出し元で、対象のオブジェクトがクラスAのインスタンスなのかクラスBのインスタンスなのか知らなければならないとしたら、その識別の処理を呼び出し元に書かなければなりません。また、同様の性質を持つクラスCやクラスDが将来作られた場合、やはり呼び出し元を修正してクラスCやクラスDを識別するコードを書く必要があります。
Protocolを使用して具体的な型を隠蔽することにより、呼び出し元のコードをそういった処理から解放することが可能になります。以下に具体例を示します。
Protocolの宣言と実装
まず、何らかの計算を行うcalculateメソッドを宣言したCaculatorProtocolがあるとします。
protocol CalculatorProtocol {
// 何らかの計算を行うメソッド
func calculate(num: Int) -> Int
}
次に、CalculatorProtocolを実装する二つのクラスを定義します。どちらもcalculateメソッドを実装していますが、一方は引数の2倍を返す処理、もう一方は引数の2乗を返す処理と、同名のメソッドで異なる処理を行っています。
class DoubleCalculator: CalculatorProtocol {
func calculate(num: Int) -> Int {
// 引数の2倍を返す
return num * 2
}
}
class SquareCalculator: CalculatorProtocol {
func calculate(num: Int) -> Int {
// 引数の2乗を返す
return num * num
}
}
さらに、typeの値に応じてCalculatorProtocolオブジェクトを返すgetCalculatorメソッドを実装したCalculatorHandlerを用意します。
class CalculatorHandler {
func getCalculator(type: String) -> CalculatorProtocol? {
if (type == "Double") {
return DoubleCalculator()
} else if (type == "Square") {
return SquareCalculator()
} else {
return nil
}
}
}
使用例(呼び出し元)
最後に、これらを使用して実際にcalculateメソッドを呼んでみましょう。
let type = "Square"
let num = 4
if let calculator: CalculatorProtocol = CalculatorHandler().getCalculator(type: type) {
let result = calculator.calculate(num: num)
print(String(result))
}
実行結果
16
呼び出し元では、calculatorオブジェクトがDoubleCalculatorのインスタンスなのか、SquareCalculatorのインスタンスなのかを判別していません。このようにしておけば、将来的にTripleCalculatorやCubeCalculatorといった新しいCalculatorProtocolの実装が現れても、呼び出し元を修正する必要がありません。このようにProtocolを活用することで抽象度を上げ、柔軟かつ保守性の高いコードを記述することが可能になります。