2014年10月24日金曜日

日付時刻も間接参照で使う

大抵のアプリでは、日付や時刻を利用します。この使い方というか、内部での作り方によって、アプリの柔軟性を大きく向上させられます。とくに、日付に関係するテストの容易性を考えると、間接参照で作るのが一番であり、間接参照しかないと断言できます。

 

一般的なアプリでは、日付の処理を、使う箇所のソースコードにベタで書いてしまいがちです。たとえば、swiftなら次のように。

// ベタで書いた日付処理
let dateNow = NSDate()
labelDate.text = dateFormatter.stringFromDate(dateNow)

こうすると、現在の日付や時刻を得る処理が、いくつもの場所に散乱してしまいます。散乱するけど、何が問題なの???と思うでしょう。もちろん、現在の日付や時刻を単に表示するだけなら、おそらく何の問題もないでしょう。しかし、日付や時刻を使って特別な処理をしたり、日付の変わり目で特殊な処理が必要な場合は、このような作り方ではテストが大変になりがちです。何の話か意味不明だと感じているでしょうが、もうしばらくお付き合いください。

 

まず、日付の取得を間接参照で作りましょう。swiftで作るなら、次のような関数を用意します。

// 間接参照で現在の日付時刻を得る関数を、1つだけ用意する
func getDateNowF() -> NSDate {
    let dateNow = NSDate()
return dateNow
}

できるだけ短くする書き方になってませんが、その辺は気にしないでください。これを使う側は、次のようになります。

// 用意した関数を使うように変更した
//let dateNow = NSDate()
let dateNow = getDateNowF()
labelDate.text = dateFormatter.stringFromDate(dateNow)

修正前に「NSDate()」としていた部分が「getDateNowF()」に変わるだけです。アプリ内の全部の日付時刻処理で、この関数を使うように作ったらどうでしょう。日付を得るときは、この関数を必ず通ることになります。まさに、間接参照です。

 

では、何のために間接参照にしたのでしょう。主な理由は、アプリのテストです。日付を得る関数が1つだけあり、そこに小さな細工をするだけで、日付関連の面倒なテストが、格段に容易となります。

まずは、最初に用意した日付取得の関数に、細工をしてみましょう。日付をシフトさせ得る値の変数「dateInterval」を追加し、その値の分だけ日付がシフトするように、関数を書き換えます。

// 変数を追加
var timeInterval : Double = 0.0
// 書き換えた関数
func getDateNowF() -> NSDate {
    let dateNow = NSDate()
    //return dateNow
    let dateShifted = dateNow.dateByAddingTimeInterval(timeInterval)
return dateShifted
}

これだけでは、また足りません。シフトする変数に値をセットする関数も追加します。正の値を設定すれば時間が進み、負の値を設定すれば時間が前に戻ります。

// シフト値の変数に値をセットする関数
func setTimeIntervalF(rTimeInterval:Double) {
    timeInterval = rTimeInterval
}

これだけで、最低限の準備は整いました。あとは上手に使うだけです。

 

テスト用のソースコードの中で、シフト値を設定する関数を呼び出しても構わないのですが、iPadアプリではユーザーが操作しながらテストすることも多いでしょう。そうすれば、画面表示を確認しながらテストできますし。

その場合には、表示するViewに小さな機能を付け加えます。数値を入力するテキストフィールドと、そのフィールドの値をシフト値に設定するボタンです。テキストフィールドに秒数を入力し、ボタンをタップすれば、アプリを起動したままでシフト値が簡単に変えられます。秒数では使いづらいので、分数または時数を入力して、シフト値に変換してから設定する方法でも構いません。

シフト値が変更できたとしても、シフト結果の日付を確認したいはずです。その日付を表示する、小さなラベルも追加します。ボタンに付けた処理では、シフト値を設定した後、シフト後の現在の日付と時刻をラベルに表示します。ここまで作ると、まあまあ使いやすくなります。テキストフィールド、ボタン、表示ラベルの3つを、日付を使うViewの端に追加するわけです。

// シフト値を設定し、シフト後の日付時刻を表示するボタン用の関数例
func testBtnShiftF(rSender: UIButton) {
    let iDouble : Double = Double(textFieldShift!.text!.toInt()!)
    setTimeIntervalF(iDouble * 60 * 60) // 秒数ではなく、時数に変換して設定
    labelDateTime.text = getDateStrNowF() + " " + getTimeStrNowF()
}  // 最後の2つの関数は別に用意したもの

準備ができたので、テストを始めましょう。日付をまたがったとき、翌日への繰り越し処理が正常に動作するかのテストです。アプリを起動した時点では、シフト値がゼロです。アプリを通常のように使って、データを入力します。ある程度のデータが入力し終わったら、シフト機能の出番です。必要な分だけプラスする値を入力して、ボタンをタップします。翌日へと変化した日付を確認できました。続けて、繰り越し処理をテストします。正常に動作したら、テストは完了ですね。

 

賢いし人は、もう気付いたでしょう。マシンの日付をまったく変えずに、日付に関係するテストができています。しかも簡単に。さらには、自由な日付や時刻へ移動するのも、一瞬で出来てしまいます。もちろん、特別なツールも必要ありません。

マシンの日付を直接変更するのは、悪影響が大きすぎます。メールの送受信では間違った日付になります。色々なアプリで作成したファイルの日付も、すべて間違ったものになります。何から何まで、日付が狂った状態です。万が一、テスト終了後に戻し忘れたら、気付くまで大変な状態になったままです。誰もが、やりたくない方法でしょう。それが、日付を間接参照することで、まったく不要となります。

 

さて、間接参照の日付時間機能は、アプリ上でどのように組み込むべきでしょうか。どこからでも呼べるべきな機能ではありますが、クラスとして定義し、アプリ内で唯一のインスタンスとして生成するのが良いと思います。例えば、クラス名が「DateController」で、インスタンスの変数名を「zDate」として生成するとか。その場合、関数はメソッドに変わり、その呼び出し方は「zDate.getDateNowF()」となります。

// 日付時間に関する機能をクラスにまとめる
class DateController {
    func getDateNowF() { ... }
    func getDateStrNowF() { ... }
    func getTimeStrNowF() { ... }
     ...
}
// クラスを使う
let zDate = DateController() // 適切なタイミングでインスタンス生成
 ...
labelDate.text = zDate.getDateStrNowF() // 必要な箇所で使う

このクラス内では、日付や時刻に関係した機能を全部入れます。現在の日付をフォーマットした文字列として返す関数とか、いろいろあるでしょう。それらの関数の中では、最初に用意した「getDateNowF」メソッド(アプリ内で唯一の日付取得機能)を呼び出し、日付時刻がシフトされた結果を返します。日付や時刻の加工処理まで含めるので、クラス外から直接「getDateNowF」メソッドを呼び出すことは、めったにないはずです。

日付加工処理まで加えたクラスは、1つのswiftファイルとして作成し、そのまま他のアプリへ持っていって使えます。自分なりの標準ライブラリとして利用すれば、新しいアプリ開発を始める際にも役立つでしょう。こうして、アプリ開発がどんどん楽になっていきます。

あと、変数名を短めにすると、使うときに楽ですね。私の場合は、変数名の先頭に「z」を付けて、続きが大文字で始まる変数名が、アプリ内で唯一の汎用的な機能と決めています。「zDate」だけでなく、「zFiles」、「zCG」、「zMail」などがあります。共通ライブラリとして作っておくことで、変更なしに使えます。機能の追加は、たまにありますね。

 

今回は、日付を取り上げましたが、もっと大きな視点での考え方があります。日付というのは、システム(アプリ)から見たら、外部からの入力機能の1つといえます。上手なシステム設計では「外部との入出力を、一カ所にまとめたほうが良い」という考え方があります。一カ所にまとめることで、何かコントロールしたいときに、直す箇所が1つで済むための配慮です。

今回はテストでの利用を紹介しましたが、日付に関する何か問題が発生したときでも、同じように一カ所の修正で対処できる可能性が高まります。日付以外でも、外部とやり取りする機能は、できるだけ1つにまとめることを心がけたいものです。

0 件のコメント:

コメントを投稿