LoginSignup
5

More than 5 years have passed since last update.

【TIPS】protocol の対応は extension で!

昔「ガチで Swift でプログラム組んで1年経っての心得」という記事で「循環参照、ダメ。絶対」の章で、循環参照の代わりに所属関係をきちんと整理して delegate を作ったほうがいい、というような内容を書きました。その時下記のようなサンプルコードもありました:

before
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 パターンを紹介するために作った必要最小限の機能しかないサンプルコードだからまだ比較的にわかりやすいですけど、実際の開発では PersonCarDelegate も中身が増えてくる可能性が非常に高いので、そうなってきた場合、もう宣言部で全て書いてしまったらどこにどれがあるのかが分かりづらくなってきて結果保守性の低下にもつながってしまうんですね

解決方法はもちろん複数あると思いますが、例えばコメントで「これはこのプロトコルのメソッドだ」とか書いてしまうこともいいですし、ただせっかく Swift は extension 機能があるので、これを使った方が Xcode にとってもも人間にとってもわかりやすいですし内容が膨れ上がってきたクラスを分割することもできますのでオススメです。それに protocol のところと同じ順番で書いていけばよりわかりやすいです。

まあ一応例を挙げますと、上記のコードを下記のように修正するだけです:

after

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 の内容だとすぐ分かっちゃいますね!

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
5