ファイルの読み書きを間接参照で作る話は一旦終わったのですが、使う際の状況をよく考えたら、リンククラスに親クラスが必要だと気付きました。その辺の話を追加で書きます。
本題に入る前に、私の普段の作り方を少しだけ。
似たようなクラスを複数作る場合、共通部分を親クラスとして最初に作り、異なる部分はサブクラスとして追加するでしょう。私も同じように作りますが、最初から親クラスを用意することはほとんどありません。似たクラスを作るかどうか不確かなので、似たクラスを作る段階になってから、親クラスを作るのが基本です。
このようにする理由は、無駄な作業を少しでも減らしたいからです。共通部分を親クラスに持つといっても、どの辺までが共通なのかは、似たクラスを実際に作る段階で明確になります。ですから、明確になったときに親クラスを作ったほうが、全体としての作業量が最小限で済みます。そもそも、似たクラスを作るかどうかが、不確実ですから。こう考えているため、最初から親クラスを作ることは、めったにありません。
で、今回のリンククラスです。いつもと同じように考えて、親クラスを用意しませんでした。
しかし、よくよく考えると、切り替え可能に作ることが大前提のリンククラスです。切り替え時の変更を最小限に済ませるなら、最初から親クラスを用意するのが、もっとも効率的だと思います。もちろん、切り替え用のリンククラスを作るとき、モデルクラスの中まで含めて変更しても構わないのですが、モデルクラスを変更せずにと考えている以上、やっぱり親クラスは必要でしょう。それに、リンククラスの仕様変更は、生じそうもないですし。
というわけで、さっそく親リンククラスを作ってしまいました。出来立てです。
まず、親クラスの役割を説明します。親のリンククラスは、リンククラスの共通部分を持っていますから、どのリンククラスにも使える機能を持ちます。そして、モデルクラスのように、サブのリンククラスを切り替えて使う側に利用されます。このような役割なので、プロトコル的なクラスと考えれば理解しやすいでしょう。
切り替えて使われるサブのリンククラスは、親クラスとは異なる機能を持つだけでなく、共通機能の実装という役割も持ちます。これら2つのうち後半の役割は、プロトコルの実装と考えれば理解しやすいでしょう。
親子クラスなのですが、リンククラスは間接参照が目的なので、プロトコル的な形になるのだと思います。でも、親子クラスとして実現するほうが、作りやすくて扱いやすいので選びました。
さて、実際のSwiftコードです。データ形式が4つありますから、それぞれに親クラスを作らなければなりません。
まずは、テキストを扱うリンククラスから。次のようなSwiftコードになりました。
// ============================== 文字列をファイルへ読み書き
// 親クラス(切り替えて利用する側で使う)
class ZSLText2Storage {
// アプリ内のリソースからロード
func loadDefaultF() -> String? {
return nil
}
// ロード
func loadF() -> String? {
return nil
}
// 保存
func saveF(rStrData:String) -> Bool {
return false
}
}
// サブクラス(インスタンスを生成する側で使う)
class ZSLText2File: ZSLText2Storage {
var cDir: ZFDir = .Docs // 仮の値
var cFileName: String = "dummy.txt"
//初期化
init(_ rDir:ZFDir, _ rFileName:String) {
cDir = rDir
cFileName = rFileName
}
// アプリ内のリソースから
override func loadDefaultF() -> String? {
let iFileNameN: String = cFileName.stringByDeletingPathExtension
let iFileNameS: String = cFileName.pathExtension
let iStr: String? = zFReadFileTextResF(iFileNameN, iFileNameS)
return iStr
}
// 指定されたディレクトリから読み込む
override func loadF() -> String? {
let iStr: String? = zFReadFileTextF(cDir, cFileName)
return iStr
}
// 指定されたディレクトリへ書き出す
override func saveF(rStrData:String) -> Bool {
let iBool: Bool = zFWriteFileTextF(cDir, cFileName, rStrData)
return iBool
}
}
親クラスには、共通する3つのメソッドだけで、初期化は含まれていません。リンククラスの初期化は、保存先が何なのかに依存するため、共通化できないからです。
3つの共通メソッドは、基本的に何もしません。ただし、処理が失敗したときの戻り値を返すように作ってあります。上書きされない場合でも、それなりに良い形で正常に動作するようにとの配慮です。「上書きされてません」とエラーメッセージを出すことも可能ですが、それはやりすぎでしょう。
サブクラスは、基本的に何も変えていません。親クラスが持つ3つのメソッドを上書きするので、メソッドの先頭にoverrideを加えただけです。機能的にも同じですし、使い方も前と変わりません。
続いて、NSDictionaryを扱うリンククラスです。こちらも親クラスを追加して、次のようなSwiftコードになりました。
// ============================== NSDictionaryをファイルへ読み書き
// 親クラス(切り替えて利用する側で使う)
class ZSLDic2Storage {
// アプリ内のリソースからロード
func loadDefaultF() -> NSDictionary? {
return nil
}
// ロード
func loadF() -> NSDictionary? {
return nil
}
// 保存
func saveF(rNSDic:NSDictionary) -> Bool {
return false
}
}
// サブクラス(インスタンスを生成する側で使う)
class ZSLDic2File: ZSLDic2Storage {
var cDir: ZFDir = .Docs // 仮の値
var cFileName: String = "dummy.plist"
//初期化
init(_ rDir:ZFDir, _ rFileName:String) {
cDir = rDir
cFileName = rFileName
}
// アプリ内のリソースから
override func loadDefaultF() -> NSDictionary? {
let iFileNameN: String = cFileName.stringByDeletingPathExtension
let iFileNameS: String = cFileName.pathExtension
let iNSDic: NSDictionary? = zFReadFileDicResF(iFileNameN, iFileNameS)
return iNSDic
}
// 指定されたディレクトリから読み込む
override func loadF() -> NSDictionary? {
let iNSDic: NSDictionary? = zFReadFileDicF(cDir, cFileName)
return iNSDic
}
// 指定されたディレクトリへ書き出す
override func saveF(rNSDic:NSDictionary) -> Bool {
let iBool: Bool = zFWriteFileDicF(cDir, cFileName, rNSDic)
return iBool
}
}
変更点は、テキストを扱うリンククラスと同じです。説明は不要でしょう。
このように親リンククラスを追加したので、一部の使い方は変更になります。それは、リンククラスを交換して使うモデルクラスです。今まではリンククラスのデータ型としてサブのリンククラスを指定していましたが、その代わりに、親のリンククラスを指定します。たったこれだけです。
モデルクラスが、親のリンククラスを扱う形になるので、すべてのサブのリンククラスを切り替えて使えるようになり、モデルクラスの変更は生じません。リンククラスの仕様が変更されない限り、モデルクラスはそのまま使い続けられます。
モデルクラス以外では、前に説明したとおりの使い方です。中央処理では、サブのリンククラスとしてインスタンスを生成し、モデルクラスに渡します。また、一括バックアップなどの目的で、サブのリンククラスを操作するときも、前と同じように操作できます。つまり、親クラスの追加で変わったのは、モデルクラス内のデータ型の指定だけなのです。
当初の予定外に、親のリンククラスを追加することになりました。ちょっと気付くのが遅かったですが、早めに気付いて良かったです。モデルクラスを使う部分は実際に作ってないため、気付くのが遅くなったのでしょう。もし作ってれば簡単に気付いたのですが。まあ、こんなことは、たまにありますね。素直に修正することが大事です。
以上のように、リンク先の切り替え部分も、将来の変更が最小限になりました。これからも、間接参照の考え方を積極的に活用して、いろいろな機能を実現したいと思います。
(使用開発ツール:Xcode 6.3.2, SDK iOS 8.3)
0 件のコメント:
コメントを投稿