独自ライブラリを作り進めていくと、関数名やメソッド名の命名ルールを思い付くことがあります。でもライブラリとして使っているので、名前を簡単には変えられません。今までに作ったアプリが、それらライブラリを使っているからです。
もし古いライブラリの関数名などを“絶対に変えたい”なら、上手に移行する手順はあります。言われてみれば当たり前の方法ですが、意外に気付かない人もいるようなので、ちょっと紹介します。
実は私も最近、新しい命名ルールを採用しました。数回前の投稿で書いたのですが、独自ライブラリの中で、グローバル関数として作ったときの名前は、「z」を付けて始めるルールに決めました。以前なら「createTxtFldF」と付けるところを、今は「zCreateTxtFldF」と付けています。これで新しいアプリは問題ないのですが、古いアプリは「createTxtFldF」が使われています。それも多数の箇所で。そこで次のように、移行作業を進めています。
まず、ライブラリ本体には、両方の名前で関数を作っておきます。どちらを使われても構わないように。具体的には、次のようなSwiftコードになります。
// テキストフィールドを作成
func zCreateTxtFldF(rText:String, rTextSize:CGFloat, rAlign:NSTextAlignment,
rX:CGFloat, rY:CGFloat, rWidth:CGFloat, rHeight:CGFloat) -> UITextField {
let iTextField: UITextField = UITextField(frame:CGRectMake(rX, rY, rWidth, rHeight))
iTextField.text = rText
iTextField.font = UIFont(name:FONT_NAME_DEFAULT, size:rTextSize)
iTextField.textAlignment = rAlign
iTextField.borderStyle = UITextBorderStyle.RoundedRect
return iTextField
}
// テキストフィールドを作成(旧タイプ)
func createTxtFldF(rText:String, rTextSize:CGFloat, rAlign:NSTextAlignment,
rX:CGFloat, rY:CGFloat, rWidth:CGFloat, rHeight:CGFloat) -> UITextField {
return zCreateTxtFldF(rText, rTextSize, rAlign, rX, rY, rWidth, rHeight)
}
見てのとおり、同じ関数を2つ作ってはいません。古い関数を新しい名前に変更し、古い名前の関数は、新しい名前の関数を呼び出す形にします。分かりやすく書くと、次のような形で変更してます。
// 関数名の変更前
func 古い関数名(引数) -> 戻り値 { ... }
// 関数名の変更後
func 新しい関数名(引数) -> 戻り値 { ... }
func 古い関数名(引数) -> 戻り値 { return 新しい関数名(引数) } // 最終的に削除
こうすると、メンテナンスするのは1つだけになり、片方を変更し忘れるミスが発生しません。もちろん、古い名前で呼び出す場合は無駄な処理が発生して少し遅くなりますが、ほとんど影響はないでしょう。もし影響があったら、その箇所を新しい名前に変更すれば済みますので。
どちらが古いのか、他人が見ても判断できるように、コメントで「旧タイプ」と入れておきます。こういった親切は意外に重要なのです。
独自ライブラリの大元は、以上のような形で、当面は両方を残しておきます。ちなみにライブラリの大元は、iOS実験専用アプリの中に作ってあって、テスト用コードも一緒に保存されています。機能拡張する場合も、iOS実験専用アプリの中で行います。アプリで使い始めるか更新する際に、ライブラリをコピーして、アプリ側に持っていきます。
ライブラリを使うアプリ側での更新は、どうなるでしょうか。当然ですが、地道な作業で、1つずつ置き換えていくことになります。次のような手順で。
まず最初に、古いアプリで使われているライブラリのソースコード(古い名前だけが含まれる)を、新しいソースコード(新旧両方の名前が含まれる)に入れ替えます。この時点で、念のためにアプリを動かしてみます。全部の機能を試す必要はなく、主な機能だけです。当たり前ですが、正常に動くはずです。
正常動作を確認できたら、いよいよ名前の入れ替えです。アプリ独自のソースコード全部で、古い名前を使っている箇所を見付け、新しい名前に置き換えます。一括置き換えでも大丈夫なはずです。ソースコード全体で古い名前を検索し、ライブラリにしか存在しなければ置き換えは完了です。この状態でも念のために、アプリを動作させて確認します。
いよいよ最後は、ライブラリのソースコードから、古い名前の部分だけを削除します。削除後も正常にビルドでき、正常に動作すれば、移行作業が完了となります。
さて、ここで疑問が生じたと思います。アプリ型にコピーしたライブラリのソースコードからは、古い名前が削除されました。でも、ライブラリの大元には、古い名前の関数などが含まれたままです。違う状態のままで構わないのでしょうか。
上手い方法が見つからないので、仕方ないと思っています。他のアプリでもライブラリを使っていて、まだ古い名前が残っている以上、大元のソースコードから古い名前を削除できません。当分の間は、残しておくしか選択肢はないのです。
ただし、ソースコードが違っているため、管理上の工夫は必要です。バージョン管理の代わりとして、アプリ側のソースコードの先頭に、コメントの形でメモを残しています。その内容は、ライブラリをコピーした日付(これがバージョン番号の代用情報となります)、削除した古い名前のグループ名(たとえば「UI部品の生成関数」とか)です。削除した名前を全部羅列するのではなく、分類上のグループ名で代用し、メモの手間を軽減しています。
このようにメモしたライブラリにも、いつか更新の時期がやってきます。改良版のライブラリと入れ替える場合です。その際、せっかくメモした内容が、新しいライブラリのソースコードにも残るように更新します。次のような手順で。
アプリ内のライブラリ入れ替えは、まず古いソースコードのファイル名を少し変更してから、新しいソースコードのファイルを追加します。その後、古いソースコードのメモ部分をコピーし、新しいソースコードの先頭へペーストしてから、日付部分を当日に書き換えます。そのメモ部分を見て、新しいソースコード内の不要な名前の部分を削除するという手順です。少し手間がかかりますが、仕方がないでしょうね。メモ部分のコピーが終わったら、古いソースコードの改名済みファイルを削除して、ライブラリの入れ替えは終了です。
以上は、関数名やメソッド名を変更した場合ですが、クラス名を変更する場合も同じ考え方が適用できます。
クラスでも、クラスの記述が1つになるように、古い名前のクラスが、新しい名前のクラスを参照する形で作ります。クラスの場合、参照する方法としてはサブクラスが適しています。分かりやすい形で書くと、次のような形式になります。
// クラス名の変更前
class 古いクラス名 { ... }
// クラス名の変更後
class 新しいクラス名 { ... }
class 古いクラス名: 新しいクラス名 { } // 最終的に削除
これだと少し分かりにくいですが、古いクラス名の中身は空で、ブロックを示す括弧記号{}で囲まれているだけです。新しい名前のクラスを継承だけして、変更箇所が何もないクラスを作るというわけなのです。
アプリ側の移行方法も、関数の場合と同様です。まずライブラリを入れ替えてから、アプリが正常に動作するから確認します。正常に動いたら、アプリ側のソースコードで、新しい名前のクラス名に置き換えます。ここでもアプリが正常に動くか確認し、正常ならライブラリ内から古いクラス名の部分を削除します。最後にアプリの動作確認をして、正常に動けば移行が終了です。
アプリ側へコピーしたライブラリのソースコードでも、コピーした日付とともに、削除した名前グループの情報を、ソースコードの先頭にメモとして残します。このあたりの考え方は、関数の場合と同じです。
今のところ、バージョン番号の代わりとして、コピーした日付を用いています。バージョン番号が好きなら、それを使っても構わないでしょう。ただし、ライブラリの種類が増えると、バージョン番号がそれぞれ違ってしまい、なにかと気分が良くありません。そんな理由もあって、コピーした日付に落ちついたというわけです。もし上手なバージョン番号の命名ルールを思い付いたら、バージョン番号に切り替えるかもしれません。
独自ライブラリとして作ったソースコードの大元は、前述のようにiOS実験専用アプリの中に入れてあります。アプリごとバックアップしていますが、経歴を残したバージョン管理はしていません。何か思い付いたら、機能を拡張したり、内部を改良したりしています。そのため、時間の経過とともに、少しずつ良くなっています。iOS実験専用アプリ内に残っているのは、常に最新状態だけです。好きなときに細かく改良するので、バージョン番号を付けづらいという面もありますね。
逆にアプリのほうは、リリースした全バージョンを、プロジェクトのフォルダごと残してあります。その中に、ライブラリのソースコードも含まれるため、こちらで経歴を残しているのと同じ効果を持ちます。アプリのほうは、アプリ全体にバージョン番号を付けていて、それが管理番号の役割も持っています。リリースするときには、バージョン番号が必要ですし。
今回は、関数銘やクラス名を変更する際の話でした。意外に大変なので、めったに変更しないことだけは表明しておきます。でも、Swiftに十分慣れた時点から作り始めたのではなく、だんだんと慣れながら作り進めたので、最初に作ったソースコードの各部名称は、変更したくなるものなのです。自分なりの命名ルールが固まってきたのは、最近ですから。
一番良いのは、過去に作ったものに関して、あまり気にしないことでしょうか。それができれば苦労しないのですけど。
(使用開発ツール:Xcode 6.3, SDK iOS 8.3)