この記事を書いた理由
Swiftを勉強し始めて、ずっと分かんなかったよ。
難しい。難しいよDelegateの理解。
というわけで、もしかしたら他にも同じ方いらっしゃるのではないかと思いDelegateについて備忘録的に私の理解を記載したいと思います。
Delegateは色々なされ方がありますが、まずは初級として概念的に理解していただければと思います。
まだ勉強中なので間違えていましたらご指摘いただけますと幸いです。
目次
1.Delegateとは?
2.どんな時にDelegateは必要なの?
3.メンドクサイから処理の一部を、他にやってもらう
4.エラーが出てしまう理由
5.protocolを使用する
6.まとめ
Delegateとは?
結論から言うと、Delegateとは 「あるクラスから他のクラスに処理を任せる」 というデザインパターンのことです。
かみ砕くと、 「自身で行う処理の一部を、他にやってもらう」こと。 この一連の流れ をDelegateと言います。
まだ分かりにくいですよね・・・
具体的に読みほどいていきましょう。
どんな時にDelegateは必要なの?
今回は簡単な計算結果をプリントする処理を見てみましょう。
まずは、以下のコードを理解してください。
コードの解説(折りたたみ)
下記は足し算結果を出す+結果をプリントするという2つの処理をするクラスを作成しています。 func printResultで、num1、num2の型を指定し、足すことを記載しています。 そしてprintを使用して、結果をプリントするようにしています。 ※AddはAddition(足し算)のことです。class AddResultPrinter {
func printResult(num1: Int, num2: Int) {
let result = num1 + num2
print("\(num1)と\(num2)を足し算した結果は\(result)です。)
}
}
AddResultPrinter().printResult(num1: 5, num2: 3)
//「5と3を足し算した結果は8です。」とプリントされます。
計算は足し算だけではないので以下の引き算も見てみましょう。
コードの解説(折りたたみ)
func printResultで、num1、num2の型を指定し、引くことを記載しています。 そしてprintを使用して、結果をプリントするようにしています。 ※SubはSubtration(引き算)のことです。引き算
class SubResultPrinter {
func printResult(num1: Int, num2: Int) {
let result = num1 - num2
print("\(num1)と\(num2)を引き算した結果は\(result)です。)
}
}
SubResultPrinter().printResult(num1: 5, num2: 3)
//「5と3を引き算した結果は2です。」とプリントされます。
足し算、引き算見比べると2つしか違いはありません。
このとき、他の計算(掛け算/割り算)を追加する場合も同じようなコードをつらつら書かなければいけません。
別にこのまま書いても大丈夫です。
ただし、もっと簡単に書けないものか・・・
この時初めて「処理の一部を、他にやってもらう」ことはできないかという発想になります。
ーーーーーーーーーーーーーーーーーーーーーーーーーーー
欲張りなあなたへ⇩
掛け算(折りたたみ)
class MulResultPrinter { func printResult(num1: Int, num2: Int) { let result = num1 * num2 print("\(num1)と\(num2)を掛け算した結果は\(result)です。) } }割り算(折りたたみ)
class DivResultPrinter { func printResult(num1: Int, num2: Int) { let result = num1 / num2 print("\(num1)と\(num2)を割り算した結果は\(result)です。) } }メンドクサイから処理の一部を、他にやってもらう
この時、それぞれの計算classの中で共通している処理はnum1とnum2の型指定と計算結果のプリントの2つですね
なので、この2つの処理を1つにまとめて処理してもらいましょう。
計算側のコード(折りたたみ)
AddCalculatorを作成し、足し算に専念するclassを作成。 次に、num1, num2、及び、num1 + num2の結果をInt型と指定した、calculateという処理を作成します。 最後に、プリントした際にこれは何算であるかをわからせるために calculationNameを作成しています。 (引き算も同様です。)//足し算
class AddCalculator {
func calculate(num1: Int, num2: Int) -> Int {
return num1 + num2
}
func calculationName() -> String {
return "足し算"
}
}
//引き算
class SubCalculator {
func calculate(num1: Int, num2: Int) -> Int {
return num1 - num2
}
func calculationName() -> String {
return "引き算"
}
}
プリント側のコード(折りたたみ)
ResultPrinterを作成し、プリントするだけのClassを作成。 つくった足し算や、引き算classを使用できるように、プロパティcalculatorを作成し、代入できるようにする。(入れるものが無いので今回はAddcalculatorを代入) プリントを実行するprintResultメソッドを作成。calculatorに代入された名前と計算結果を引き継げるように name と result を作成。class ResultPrinter {
//以下を差し替えればよさそう。(入れるものが無いので、いまはAddCalculator()をいれている)
var calculator = AddCalculator()
func printResult(num1: Int, num2: Int) {
let name = calculator.calculationName()
let result = calculator.calculate(num1: num1, num2: num2)
print("/(num1)と/(num2)を/(name)した結果は/(result)です")
}
}
では、上記の組み合わせで実際に表示してみましょう。
let printer = ResultPrinter()
printer.printResult(num1: 8, num2: 5)
コンソールに「8と5を足し算した結果は13です」と表示されるはずです。
では、引き算もやってみましょう。
以下を記載してみます。
let printer = ResultPrinter()
printer.printResult(num1: 8, num2: 5)
let subCalculator = SubCalculator()
printer.calculator = subCalculator
printer.printResult(num1: 8, num2: 5)
すると、printer.calculator = subCalculator の横に「Cannot assign value of type 'SubCalculator' to type 'AdditionCalculator'」とエラーが発生してしまいます。
エラーが出てしまう理由
上記で出てしまったエラーは、printer.calculatorはSubCalculator型ではないですよ~って言ってるわけです。
さかのぼって、プリント側のコード(折りたたみ)を見ると、var calculator = AddCalculator() と記載してますね。これが原因となります。
var calculator = SubCalculator() としてもいいのですが、今度はAddCalculatorが使用できなくなってしまいます。
これは、変数には同じクラスのインスタンスしかセットできないということが問題です。
しかし今は処理を分けたいので、同じメソッドを持っているクラスのインスタンスであれば以下のように切り替えをしたいです。
受け売りですが、例えるならばUSB端子の形状が同じなら、どんなメーカーの製品でもPCに接続できて欲しいといった状況です。
実は、それを実現するのがprotocolとなります!
protocolを使用する
protocolはDelegateには欠かせぬ存在です。
protocolを使用することで、「同じメソッドを持つクラスを同等に扱う」ことができます。
使用するためには、①protocolの作成 ②同等に扱いたいclassと計算処理を代入するプロパティへ、作成したprotocolを付与を行います。
以下を理解しましょう。
①protocolの作成
protocol Calculator {
func calculate(num1: Int, num2: Int) -> Int
func calculationName() -> String
}
これは、上記2つのメソッドを兼ね備えているものは、Calculatorとして扱える。といった記述になります。
②作成したprotocolの付与
//AddCalculatorにCalculatorプロトコルを付与
class AddCalculator: Calculator {
func calculate(num1: Int, num2: Int) -> Int {
return num1 + num2
}
func calculationName() -> String {
return "足し算"
}
}
//SubCalculatorにCalculatorプロトコルを付与
class SubCalculator: Calculator {
func calculate(num1: Int, num2: Int) -> Int {
return num1 - num2
}
func calculationName() -> String {
return "引き算"
}
}
//ResultPrinter内の、差し替えプロパティ(calculator)にもCalculatorを忘れずに付与
class ResultPrinter {
var calculator: Calculator = AddCalculator()
func printResult(num1: Int, num2: Int) {
let name = calculator.calculationName()
let result = calculator.calculate(num1: num1, num2: num2)
print("/(num1)と/(num2)を/(name)した結果は/(result)です")
}
}
上記のように記述することで、切り替えができるようになります。
イメージは以下のような感じ
試しに、また以下を記載してみましょう。
let printer = ResultPrinter()
printer.printResult(num1: 8, num2: 5)
let subCalculator = SubCalculator()
printer.calculator = subCalculator
printer.printResult(num1: 8, num2: 5)
すると、「8と5を足し算した結果は13です」「8と5を引き算した結果は3です」と正しく表示されるはずです。
これでデリゲートは完了しています。
まとめ
一番最初にDelegateとは「自身で行う処理の一部を、他にやってもらう」と記載しましたが、このようにprotocolを使用して、処理の一部を他に任せることをDelegateと言います。
勘違いして欲しくないのは、Delegate自体が特殊なコードとかではないことです。流れのことです。
長々書きましたが、必ず使用するのでprotocolとは?、Delegateとは?をしっかり理解しておくといいかもしれません。