2014年10月22日水曜日

間接参照を幅広く使う

前回は、swiftでUI部品を生成する関数の話をしました。その原点となる考え方を、他の話よりも先に紹介したほうが良いでしょう。その考え方とは「間接参照」です。柔軟性の高いソフトウェアを作る上で、おそらく一番大事な考え方だと思います。仮にオブジェクト指向を知らなくても、間接参照を上手に使えば、柔軟性の高いシステムが作れるほどですから。

 

UI部品を生成する関数ですが、別の視点から見てみましょう。通常、UI部品の生成を、使う箇所にベタで書いた場合、使う箇所とUI部品の生成コード(UI部品クラスのインスタンスを生成するコード)が直接つながっています。しかし、生成関数を使うと、使う箇所は生成関数につながり、生成関数が生成コードとつながります。つまり、使う箇所と生成コードの間に生成関数が挿入され、使う箇所と生成コードが、生成関数を通じて間接参照されていることになります。生成関数とは、間接参照を実現するための方法でもあります。

参照関係の間に関数が入ることと、関数内にソースコードを記述できることにより、単なる参照よりも柔軟な可能性が生まれます。UI部品のデフォルト値をまとめて変更できるだけでなく、新しい機能を加えたりが、関数部分の変更だけで実現できるのです。GUIの設計ツールなども、生成するUI部品を参照してはいますが、まとめて機能を変更することはできません。関数を用いた間接参照だから、可能になるわけです。

 

間接参照は、幅広く利用可能です。もっとも単純な、間接参照の例も挙げてみましょう。よく使う、色の定義です。文字の色、背景の色、枠線の色など、GUIでは必ず設定する属性の1つでしょう。一番ありがちな作り方は、ソースコードの中に、ベタで色を指定する方法です。swiftなら次のように。

labelMessage.textColor = UIColor.redColor()
labelMessage.text = "エラーが発生しました。"

このように作ると、何かの色をまとめて変えたいとき、修正箇所が数多くなってしまいます。さらに、修正箇所がどこにあるのか探すのは大変で、修正漏れが発生しやすくなります。

こんなときも役立つのが、間接参照です。色に名前をつけて定義し、それを参照する形で色を指定します。swiftなら「let」を使って次のようになります。

// どこかで事前に定義
let COLOR_BLACK = UIColor.blackColor()
let COLOR_WHITE = UIColor.whiteColor()
let COLOR_BLUE = UIColor.blueColor()
let COLOR_GREEN = UIColor.greenColor()
let COLOR_RED = UIColor.redColor()
let COLOR_CLEAR = UIColor.clearColor()
// 使う側
labelMessage.textColor = COLOR_RED

実は、このような作り方は悪い例です。色の名前のまま定義するだけでは、色を変更するときに、この定義を使っている箇所を書き直さなければなりません。「COLOR_RED」と書いてたのを、「COLOR_BLUE」に直すといったように。これでは、柔軟性の高い作り方ではありませんよね。

 

もっと賢い作り方があります。色の名前ではなく、色の役割の名前で定義する方法です。たとえば、注意レベルの違いを色分けするとき、次のように定義することができます。

// どこかで事前に定義
let COLOR_NORMAL = UIColor.blackColor()
let COLOR_OK = UIColor.greenColor()
let COLOR_CAUTION = UIColor.blueColor()
let COLOR_ERROR = UIColor.redColor()
// 使う側
labelMessage.textColor = COLOR_ERROR

このように作ると、どうでしょう。エラーの色を赤からオレンジ色に変えたいとき、「COLOR_ERROR」の色指定だけ修正すれば良く、「COLOR_ERROR」を使っている全箇所は触らずに済みます。エラーの色を定義し、それを間接参照する形で作ったからこそ、1カ所の変更で全部を変えられるようになったわけです。色の変更では、APIで用意された色だけでなく、RGB値による色指定も可能です。色の微妙な変更も、修正するのは一カ所だけです。

上記の作り方では、色の名前ではなく、色の使い分けの目的別に定義しています。通常の状態が「NORMAL」、処理成功が「OK」、ちょっとした注意を示すのが「CAUTION」、より重大なエラーを伝えるのが「ERROR」です。これら4つを組み合わせて使うことになります。

 

使い分けの目的別に作ると、同じ色(特に黒色)を何個も定義することになります。それでも構わないどころか、それが普通です。もう1つ、よく使う色の定義を挙げましょう。

let COLOR_SELECTED = UIColor.blueColor()
let COLOR_UNSELECTED = UIColor.blackColor()

名前を見て分かるように、選択状態と非選択状態の色を定義しています。ここの「COLOR_UNSELECTED」も、前述の「COLOR_NORMAL」も、色としては黒を指定していますが、使う目的は異なります。目的が異なるから、同じ色の指定なのに、別々に用意しているわけです。ソースコードの各箇所でどちらを使うのかは、使用する目的に合わせて選びます。

 

選択と非選択の色の定義では、どこに適用するかで使い分けることが重要です。何の選択に使うのかで、別々の名前を定義しておくと、後から何カ所も修正するといった面倒を防止できます。

// 一般的な選択と非選択の色
let COLOR_SELECTED = UIColor.blueColor()
let COLOR_UNSELECTED = UIColor.blackColor()
// 削除する場合の選択と非選択の色
let COLOR_DELETE_SELECTED = UIColor.redColor()
let COLOR_DELETE_UNSELECTED = UIColor.blackColor()

複数の色分けを、どのように使い分けたら良いかは、後々発生しそうな修正を予測しながら考えてみてください。

分ける場合の基本としては、UI部品の生成関数を複数作ったときと同じです。一緒に変わってほしくないものを分けるだけです。たとえ今は同じ値であっても、違う設定に変わりそうだと感じたなら、別々に用意するのが賢い選択でしょう。

 

色の指定は、多くの箇所に現れます。ここでは色の事前定義による作り方を紹介しましたが、すべての色指定を、事前の色定義にすべきとは思いません。前回のUI部品の生成関数のように、その関数自体が間接参照の考え方で作られている場合、その関数内の色しては、事前の色定義を使わなくても構わないでしょう。ただし、何かの目的で別な色と統一したい場合は、生成関数の中でも、事前の色定義を使うことがあります。

 

単純な色指定の話でも、長くなってしまいました。後から色を変えたいというのは、よくある話です。それだけに、今回紹介したような作り方は重要で、無駄な作業を減らしてくれます。

間接参照を使った作り方は、あまりにも有効なので、これから何度も登場すると思います。間接参照という説明が入ってなくても、間接参照の考え方で作られている場合も多いので、間接参照の考え方を頭の奥底にでも入れておくとよいかもです。

0 件のコメント:

コメントを投稿