昔「ガチで Swift でプログラム組んで1年経っての心得」という記事で「循環参照、ダメ。絶対」の章で、循環参照の代わりに所属関係をきちんと整理して delegate
を作ったほうがいい、というような内容を書きました。その時下記のようなサンプルコードもありました:
class Person: CarDelegate {
var car: Car?
func goForADrive() {
self.car?.drive()
}
func buyGasoline() {
print("Go buy some gasoline")
}
func solveGasolineShortageProblem() {
self.buyGasoline()
}
}
class Car {
var delegate: CarDelegate?
func drive() {
print("Drive!")
}
func outOfGasoline() {
self.delegate?.solveGasolineShortageProblem()
}
}
protocol CarDelegate {
func solveGasolineShortageProblem()
}
let nishikinoMaki = Person()
let ferrari458Italia = Car()
nishikinoMaki.car = ferrari458Italia
ferrari458Italia.delegate = nishikinoMaki
nishikinoMaki.goForADrive()
ferrari458Italia.outOfGasoline()
もちろんこの記述自体に別に「これはダメ」というようなものは多分ないと思いますが…(ないよね?)ところが、最近の開発で一つ気付いたことがあります:
対応してるプロトコルが増えてくるとプロトコルのメソッド増減するたびにそれに対応してるクラスや構造体の修正が面倒!
まあ当然ですよね、この例で見ても、下の protocol CarDelegate
見るまで、Person
はどこで CarDelegate
を対応させてるのかわからないでしょう?しかもこれはあくまで delegate
パターンを紹介するために作った必要最小限の機能しかないサンプルコードだからまだ比較的にわかりやすいですけど、実際の開発では Person
も CarDelegate
も中身が増えてくる可能性が非常に高いので、そうなってきた場合、もう宣言部で全て書いてしまったらどこにどれがあるのかが分かりづらくなってきて結果保守性の低下にもつながってしまうんですね
解決方法はもちろん複数あると思いますが、例えばコメントで「これはこのプロトコルのメソッドだ」とか書いてしまうこともいいですし、ただせっかく Swift は extension
機能があるので、これを使った方が Xcode にとってもも人間にとってもわかりやすいですし内容が膨れ上がってきたクラスを分割することもできますのでオススメです。それに protocol
のところと同じ順番で書いていけばよりわかりやすいです。
まあ一応例を挙げますと、上記のコードを下記のように修正するだけです:
class Person {
var car: Car?
func goForADrive() {
self.car?.drive()
}
func buyGasoline() {
print("Go buy some gasoline")
}
}
extension Person: CarDelegate {
func solveGasolineShortageProblem() {
self.buyGasoline()
}
}
class Car {
var delegate: CarDelegate?
func drive() {
print("Drive!")
}
func outOfGasoline() {
self.delegate?.solveGasolineShortageProblem()
}
}
protocol CarDelegate {
func solveGasolineShortageProblem()
}
let nishikinoMaki = Person()
let ferrari458Italia = Car()
nishikinoMaki.car = ferrari458Italia
ferrari458Italia.delegate = nishikinoMaki
nishikinoMaki.goForADrive()
ferrari458Italia.outOfGasoline()
これで extension Person: CarDelegate
を読めば solveGasolineShortageProblem()
は CarDelegate
の内容だとすぐ分かっちゃいますね!