noki雑記

iOS、ときどきAndroid

デザインパターンの基本をやってみて

普段作っているアプリを、変更に強く柔軟な作りにしたいなと思い、「Java言語で学ぶデザインパターン入門」を読んでみました。恥ずかしながら、デザインパターンを意識したことがあまりなかったので、実践例がいまいちイメージできないものもありましたが、読んでみた感想をまとめてみます。

抽象化(透過的に)すべきか考える

本書では抽象化をうまく使い、変化に強そうなコードに仕上げています。そういったコードを読んでいると、「こういうコード書いてみたいなぁ」と思う箇所がいくつもありました。そのせいか、複数のクラスに何かしら共通部分が多かったり、if文やswitch文の分岐が多くなってきた場合など、抽象化すべきかを考えるようになりました。仕様が漠然としていたり、共通部分が曖昧な場合は無理に抽象化すべきではありませんが、それでも一考するようにしています。

分割・移譲

オブジェクト指向でよく目にする「単一責任」と似たことですが、クラスを分割しておくことで目的が明確にし、修正範囲を限定することができます。また、自分が出来ない(役割ではない)部分に関しては移譲を使うことで、責任範囲を明らかにする手もあります。実際には単一責任のように細分する必要が無いケースもあると思いますが、複雑になりそうな(なってきた)場合は一考してみる必要がありそうです。

ライブラリやユーティリティに応用してみる

Swift2 系から Swift3 へのアップデート作業を行っていたときに、Swift3に対応していないライブラリがあったりして、ちょっと困ったことがありました。ライブラリは便利なので有効活用していきたいと思う一方で、活用している割合が少ないライブラリに関しては自作しても良いかなと思っています。

例えば通信まわりで有名な Alamofire ですが、単純な GET や POST だけを行うアプリであれば、URLSession で作ってしまうとか。その場合、APIの定義とうまく合わせたり、通信成功・失敗を外から扱いやすくしたりなど、考慮することは多いです。しかし、再利用性は高くなると思うのでチャレンジしてみたいです。

普段から使用していたパターンも多い

標準コンポーネントで使われているものや、考え方が似ているものを自作しているものも含め、ある程度の規模のプロダクトになるといくつかのデザインパターンが使われています。Delegate、Singleton、Prototype、Builder、Observer、Iterator パターンなどなど。ただ、それぞれ適材適所だと思っていて、例えば Singleton で作るのか Static な Class もしくは構造体で作るのか、Delegate なのか Notification なのかなど、何が適しているかは吟味したいところです。

Singleton

Singleton が必要なケースとしては、インスタンスが1つのみ必要で、複数存在してはならない場合だと思います。外から複数インスタンスを生成できてしまうと Singleton の意味が無くなってしまいます。そのため、Singleton を構造体で作ってしまうのも意味がなくなってしまいますね。構造体は値型なので、オブジェクトを複数生成してしますと参照先がばらばらになってしまいます。ユーティリティ的な役割が必要であれば、Static なメソッドを持つクラスや構造体で良いと思うし、どちらかというと構造体で作ってしまった方が分かりやすい気がしています。

Adapter と Delegate

きちんと理解できている人にとってはアホみたいな話かもしれませんが、Adapter と Delegate パターンの区別で迷ったので自分なりにまとめてみます。

Android には RecyclerView.Adapter など、Adapter という名前のついたクラスがあるので、Adapter パターンなのだろうと適当に思っていました。しかし、Delegate パターンと見比べてみると、RecyclerView.Adapter の使い方は UITableViewDataSource などと似ているなと思い始めました。

Delegate パターンの場合、既存クラスが Delegate クラス(インタフェースや抽象クラス)を利用することを前提として作られているのに対し、Adapter パターンを適用する場合は、そういった用意がされていないクラスと、処理に必要なデータなどを結びつける(変換する)機能を実装する目的があります。

そう考えてみると、Delegate パターンは UIKit などでもよく使われているので分かりやすいのですが、Adapter パターンはイメージしにくいです。特に Swift であれば、必要な箇所で private extension を使用することで Adapter パターンのようなことができてしまうと思います。stored property が必要であれば別ですが。Java で実装する場合だと、既存クラスのサブクラスを用意し、ターゲットとなるインタフェース等を implement するところですね。

Mediator

画面仕様が複雑になればなるほど、管理すべき対象が散らばっている状況は避けたくなります。Mediator パターンを使うことで、考え方がシンプルになりそうな気がしました。複数の部品が絡まっている複雑な処理を、Mediator 役がまとめるパターンですね。例としてログインフォームが出てきましたが、少なからずありそうな状況です。実際にはバリデーションに引っかかった項目を赤くするとか、通信中、データが無い時など、いろいろまとめられそうですね。

State

使ってみたいというか、実装する際に検討してみたいパターン。 UIはほぼ同じものでも、状況によって処理を分けることがあります。通信中やソフトウェアキーボードが表示されている時、アプリ全体で使いまわすデータに状態があるときなど、状態管理用の変数を用意しようとした時にちょっと考えてみようかと思います。

Strategy、Decorator

これも検討してみたいパターン。実践例があまり思いつかなかったのですが、例えばECサイトなんかでカートに入れた商品の合計額を求める場合、各商品の値段の合計やクーポンでの割引、大人一般か学生か小人か、誕生日や記念日での割引、期間限定での・・・など、いくつもの条件を計算に入れなければならない場合に使えないかなと考えてみました。

計算や条件式が複雑になるケースってたまにありますよね。なんとかしたいなと思いつつ、実装している最中に仕様が変わったりするケースもあるので、なかなか設計が難しかったりする時もあります。そういった時にも何かしらパターンを見つけられればなと思っています。

まとめ

デザインパターンは設計を考える際の1つの材料ですが、特定のパターンを使わないまでも、その考え方を身に着けておくことは非常に有意義だと感じました。オブジェクト指向の技術書を読んでいても、柔軟な設計にするためにいくつかのデザインパターンを使用するケースが出てきます。しかし実際の業務では、技術書に出てくる例よりも複雑な状況は多分にありうるので、慣れるまでは複数のパターンの吟味をし続けることになるかと思います。チームで開発していく以上、自分の設計をメンバーに理解してもらう責任があるので、やはりこういった考え方は重要で、共通認識としてのパターンを知っておくことも必要だと思いました。