Kikuchy's Second Memory

技術のこととか、技術以外のこととか、思ったことを書き留めています。

モジュール設計のGood UI/ Bad UI

意図しない誤操作を誘発したり、意図する操作を実行することが困難だったりするUIのことを、私はBad UIと呼んだりしています。

一度プロダクトとしてBad UIが出来上がって、出荷もされてしまうと、使う側はプロダクトのUIを変えることができないので、どうにか利便性を向上させようと工夫をこらします。

で、ここがBad UIのBadたる所以ですが、利便性を上げようとしても、どうも頓珍漢な方向に人間を導くのです。
時々Twitterなどで見かける、説明書きのシールが所狭しと貼られたセブンカフェのコーヒーメーカーとかが顕著な例だと思います。


http://twicolle.com/wp-content/uploads/2015/01/B8f9m6aCAAAnInF.jpg
引用元: twicolle.com


どうも人というのは、安易に言葉による説明を行いがちな気がします。
セブンカフェの例であれば、言葉で全てを表示しようとするから、上記写真のように愉快な悲惨なことになるのではないでしょうか。

ではBad UIの反対のGood UIはどんなものかと言いますと、私個人としてはレゴブロックが一番よいUIであると思っています。

http://s3-ap-northeast-1.amazonaws.com/topicks/article_thumb/21181_original.jpg
引用元: http://topicks.jp/21181

いかにも組み合わさりそうな、凸部分に凹部分があります。
それらをを押し当てれば、想像通り、二つのブロックがくっつく。
そこに言葉はいらない訳です。見るか触るかすれば機能と使いかたを想像できます。
説明不要&期待通り。それがGood UIだと考えています。



さて、プログラミングについても同じことが言えるのではないでしょうか。

ある働きをするクラスやメソッドの入力値にかかる制約を、「ドキュメントコメントに書いているから」とそれで満足していませんか?

// Bad API


/**
 * 日時から曜日を算出します。
 *
 * @param year     年。正の整数
 * @param month 月。0-11の値、1月は0、12月は11
 * @param day      日。1以上31以下の整数
 */
public String nameOfDayOfWeekFromDate(int year, int month, int day) {
    ...
}

/**
 * 曜日が休日かどうか判断します。
 *
 * @param dayOfWeek 曜日名。"mon", "tue" などの省略形
 */
public boolean isHoliday(String dayOfWeek) {
    ...
}

...

// 書いた人以外の人が読んだらどう思うでしょうか。
// 引数に数値ならなんでも入っちゃうけど大丈夫?
// 10月なんだろうけれど、1を引いている意図は?
// 戻り値は一体どんな形式?
String dow = nameOfDayOfWeekFromDate(2016, 10 - 1, 20);

// 引数には曜日入れるっぽいけれど、どんな文字列入れていいの?
if (isHoliday(dow)) {
    ...
}


あるいは、レゴブロックのように「組み合わさるからOK」というインターフェースにはできないでしょうか。

// Good API

/**
 * 曜日を表現します。
 */
public enum DayOfWeek {
    MONDAY,
    TUESDAY,
    ...
}

/**
 * 日時から曜日を得ます。
 *
 * @param date 日付
 */
public DayOfWeek dayOfWeekFromDate(Date date){
    ...
}

/**
 * 曜日が休日かどうか判断します。
 *
 * @param dayOfWeek 曜日
 */
public boolean isHoliday(DayOfWeek dayOfWeek) {
    ...
}

// 引数はDateをとる…日付が必要なんだな。考えることが少なくて良い。
// 戻り値は曜日を表すわけだな。表現形式とか気にしなくていい。
DayOfWeek dow = dayOfWeekFromDate(new Date());

// DayOfWeekオブジェクトしかはいらないから、これを入れよう。おっ、ちゃんと動くぞ!
if (isHoliday(dow)) {
    ...
}

レゴブロックの凹凸に相当するもの。

です。

intやStringなど、汎用的すぎる型で必要なデータを取りそろえようと思ったら、制約条件をコメントに書くしかない、使う人に読んで理解して記憶して気をつけておいてもらわないといけない。
すなわち、使う側の人間に認知的負荷をかけなければいけない、ということになります。

そこで、もう少し表現を特殊化した型を用意してはいかがでしょう?
「この引数にこのメソッドの戻り値が当てはまるから入れて見る」という、ほとんど考えなくてもできるような作業でも、その結果として適切に動く可能性を飛躍的に向上することができるのです。
しかもチェックするのはコンパイラわざわざ人の力を借りる必要はありません

全てが型でどうにかなるわけではないですが、それでも使える範囲で使っていきたいものです。

最近ではKotlinやSwiftが型でnullableであることを表現できる(Optinal型)ようになっているので、null値を許容するのかしないのか、安全なのかそうでないのか、これも是非とも型で表現したいですね。してくださいマジで。