【Swift】いちばん手軽なリファクタリングの方法

コードの見通しを良くする方法として、SOLIDを意識したコードを書いたり、DDDなどのアーキテクチャを導入したりといったことがありますが、それができない状況もあったりするし、そこまで重たくせずにリファクタリングできないかなと思うことがあります。

そこで、最近私がよく使っているリファクタリングの方法が、すごく手軽で、しかも結構効果的だったので紹介したいと思います。

簡単な例

<リファクタリング前のコード>

    override func viewDidLoad() {
       super.viewDidLoad()

       navigationItem.title = "title"
       let rightBarButtonItem = UIBarButtonItem(title: "検索", style: .plain, target: self, action: #selector(tappedSearch))
       navigationItem.rightBarButtonItem = rightBarButtonItem
       let tap = UITapGestureRecognizer(target: self, action: #selector(tappedView))
       tap.delegate = self
       self.view.addGestureRecognizer(tap)
       tableView.register(UINib(nibName: cellName, bundle: nil), forCellReuseIdentifier: cellName)
       tableView.dataSource = self
   }

これを、こんなふうにします。

<リファクタリング後のコード>

    override func viewDidLoad() {
       func initNavigation() {
           navigationItem.title = "title"
           let rightBarButtonItem = UIBarButtonItem(title: "検索", style: .plain, target: self, action: #selector(tappedSearch))
           navigationItem.rightBarButtonItem = rightBarButtonItem
       }
       func setGesture() {
           let tap = UITapGestureRecognizer(target: self, action: #selector(tappedView))
           tap.delegate = self
           self.view.addGestureRecognizer(tap)
       }
       func initTableView(){
           tableView.register(UINib(nibName: cellName, bundle: nil), forCellReuseIdentifier: cellName)
           tableView.dataSource = self
       }
       super.viewDidLoad()
       initNavigation()
       setGesture()
       initTableView()
   }

各処理をメソッドで切り出しただけです。
どうでしょうか?

funcの中にfuncを書くnested functionsを使うあたりに好みが分かれるかもしれませんが、viewDidLoadで行っていることの流れが掴みやすくなったのではないでしょうか。

これは、「メソッドの抽出」と呼ばれるごく基本的なリファクタリングです。ただ、一般的なメソッドの抽出では書いてるメソッドの外に抽出するので、処理があちこち散らばってしまって読みづらくなることがありました。

この方法なら1つのfuncの中に閉じているので、処理はまとまって読むことができます。

もちろん、単一責務の法則を考えると、メソッドが肥大化したら別のクラスに切り分けたほうが良かったりしますが、それをやると結構手間もかかったりしますよね。かといって、メソッドの中にずらずらと長い処理を書いてしまうことも避けたいです。

この手法なら、コードを書く速度をほとんど落とさずに利用できます。

メソッドを2つの視点から見ること

この手法を使っていくと、さらにこんなふうに書くことがあります。

    @objc func tappedSearch(_ sender:UITapGestureRecognizer) {
       func resetData() {
           dataSource = []
           loaded = false
       }
       resetData()
   }

ここでは、resetDataだけが呼ばれています。これはさすがにやりすぎ感があるかもしれません。

でも、私はこの書き方が好きです。

というのは、メソッドの利用者側からの視点と、メソッドの内部の実装の視点が意識的に書き分けられているからです。

このコードを読むと、tappedSearchは利用者側からは「検索ボタンを押したときになにかの処理をしてもらう」ためのもの、と読めます。

また、内部の実装は、「データのリセットをする」ことをしていることが読み取れます。

もし、このように分けて書かないと、検索ボタンを押す⇒データをリセットする、というつながりの理解が失われてしまいます。

そうすると後々の機能追加で、データをリセットする処理に新しい要素を加える必要があったのに抜けがでてしまうおそれがあります。

本当の効能

私はこれまで何度もこの方法を使って、実際の仕事で様々な処理を書いてみたのですが、この方法を使うと上に書いたこと以外に様々な効能があるようです。

まず、処理に名前が付きます。
処理に名前がつくと、一旦立ち止まって考える機会が生まれます。

最初の例なら、initNavigationというメソッドを作りましたが、これをinitViewにしたらどうだろうか?などと考え始めます。initViewだと少し単位が大きすぎる感じがしますね。

そんなふうに整理していくと、抽出したメソッドのレベル感が揃ってきます。

レベル感があっていくと、ここは他のメソッドからも流用できるようにしようとか、そういうことも考えられるようになってきます。

このように整理していくと、クラスやメソッドよりは小さい単位だけど、コードの1行1行よりは大きい単位に名前がつくことになります。

ちょうど頭で把握しやすい単位になっていきます。

画像1

自分がやっていることを自然に整理できていくので、無駄な処理や、いびつになっている処理もすぐに分かります。

その結果、コーディングの質もスピードも上がったような気がします。

コーディングとは分類して整理すること

コーディングをしていて感じるのは、「これは整理をしているんだな」ということです。

画面要素やデータベースの情報、それらを扱う処理などを適切に分類し、それらの関係性を整理していくと柔軟で安定したコードが書けるようになります。

となると、少しでも処理のカタマリに名前をつけていくことは、よりよいコードを書くことに繋がるのではないでしょうか。

この記事が気に入ったらサポートをしてみませんか?