『TypePadクエスト』第3話:カスタマイズですね。わかります。
パッケージ製品のカスタマイズ
そもそもソフトウェアにおける「パッケージ製品」ってなんでしょうか? なかなか一言で説明するのは難しい概念ですね。もしも就活中の学生さんに質問されたとしたら、わたしは以下のように説明します。
「紳士服売り場でハンガーにかかっている状態のスーツ」のこと。
例えば、人間の「からだ」にはそれぞれ個性があるから、ハンガーに掛かったままの状態だと、すそが長かったり、おなか周りが苦しかったりします。そんなときは、すそを詰めたり、アジャスタつきのパンツをご提案します。そういう「お客様の事情に製品を合わせる」のが僕らのお仕事です。
カスタマイズの量を最小限にしてみたら
さて、パッケージ製品のカスタマイズには、「まったく新規に開発する」場合と違って、注意すべき重要なポイントが存在します。それは、「パッケージのバージョンアップを考慮してカスタマイズする」ということです。以下に具体例を記します。
例えば、お客様の「ビジネスロジック」を実現させるために、以下の3つの方法があったとします。
方法1 ファイル「AAA」「BBB」「CCC」の該当箇所を数行修正して実現する
方法2 ファイル「AAA」だけで、100行程度修正して実現する
方法3 ファイル「DDD」を新規に追加し、200行で実現する
3つの方法の中で、最小限の労力で実現できるのは、もちろん「方法1」です。しかし1には、大きな落とし穴が待っているのです。それは、「パッケージのバージョンアップ」のお話が怖くなるのです。
ありがたいはずなのに……
パッケージのバージョンアップは、開発会社にとっては、とってもありがたいお話です。しかし「方法1」のように、「AAA」「BBB」「CCC」の3ファイルに渡って修正していると、パッケージのバージョンアップによって、3ファイルのどれかに修正が入るかもしれません。
仮に、「CCC」ファイルに修正が入ったとします。すると、「お客様のビジネスロジックを実現させるために変更したコード」なのか、「パージョンアップによって変更したコード」なのか複合的にロジックが混ざってしまい、わけが分からなくなってしまうのです。昔の自分による「安易な判断」を猛烈に反省し、ひたすらコードと格闘する夜が続きます。そうです。ここでは新規にファイルを追加して200行で実現する「方法3」を採用し、パッケージ標準ファイルへの変更を避けるべきだったのです。
実践
簡単なサンプルを叩き台にして、実際にカスタマイズしてみます。お客様の要望は以下の通りです。
「パンチボタンを押したら、ジャブに続いて昇龍拳を表示してほしい」
パッケージ標準のファイルの内容は以下の通りです。
1 |
function Punch() |
1 |
<%@ Page Language="C#" AutoEventWireup="true" Inherits="System.Web.UI.Page" %> |
この状態からカスタマイズしていきます。
お客様に言われた通りにただ実装するだけなら、「common.js」の内容を修正すればOKです。しかし「common.js」は名前からして、他の「aspx」ファイルからも利用されている可能性が高いです。
何を恐れているかというと、「Original.aspx」では「パンチボタン」から呼び出されていましたが、他の「aspx」では「ジャブボタン」から呼び出されているかもしれません。単純に「Punch」メソッドの内容を書き換えてしまうと、「ジャブボタン」を押した場合にも、「昇龍拳」が表示されてしまいます。
1 |
function Punch() |
そこで「common.js」は変更せずに、「aspx」ファイルにカスタマイズを加えます。具体的には以下のようにします。
1 |
<%@ Page Language="C#" AutoEventWireup="true" Inherits="System.Web.UI.Page" %> |
カスタマイズ対象の「aspx」ファイルに、改めて「Punch」メソッドを定義し直しました。
ここで重要なポイントが2つあります。
1つ目は、「Punch」メソッドの再定義は「common.js」の後に追加する必要があるということです。JavaScriptのメソッドの定義は、後出しが勝ちますので、「common.js」よりも先に定義してしまうと、「common.js」の定義で上書きされて「ジャブ」のみの動作になってしまいます。
カスタマイズの真意を問う
2つ目のポイントは、お客様から依頼された「カスタマイズの真意」を確認しておくことです。つまり「お客様はなぜ、昇龍拳を追加したいのだろうか」という意識をもって、仕様を詰めていく必要があるということです。例えば今回のカスタマイズの場合は、以下のような可能性が考えられます。
● 可能性1 「ジャブ」だけの攻撃じゃ寂しいから、「昇龍拳」も追加しておくか
● 可能性2 やはり攻撃の最後は「昇龍拳」でフィニッシュしたいね
もしもお客様の真意が後者だったと確認できた場合、「Override1.aspx」のカスタマイズでは不十分です。それでは一体、何が足りないのでしょうか? 答えは「パッケージのバージョンアップを考慮していない」ことです。
バージョンアップしたら不具合発生
お客様の依頼によってパッケージのバージョンアップを行いました。バージョンのアップに伴い「common.js」の内容にも変更があったようです。
1 |
function Punch() |
この状態で「Override1.aspx」でパンチボタンを押した場合、「ジャブ」「昇龍拳」が表示されます。バージョンアップ前と変わりません。しかしお客様の認識では、「ジャブ」「ストレート」「フック」「アッパー」「昇龍拳」が表示されると思っています。そのため、現在のカスタマイズ状態では不具合が発生していると指摘されます。
ベースを呼び出す
「Ovdrride1.aspx」のカスタマイズは失敗でした。それでは、「バージョンアップ前」と「バージョンアップ後」の両方で、「お客様の認識通りに動くカスタマイズ」をするには、一体どのようにすれば良いのでしょうか。具体的には、以下のように修正します。
1 |
<%@ Page Language="C#" AutoEventWireup="true" Inherits="System.Web.UI.Page" %> |
まずは「basePunch」にパッケージ標準の「Punch」メソッドを保存しておきます。そして、新たに定義した「Punch」メソッド内で、「basePunch」を呼び出します。
ポイントは、「scriptタグ」を2つに分割し、「basePunch」への保存と、新しい「Punch」の定義を分けることです。もしも同じscriptタグ内に書いてしまうと、再帰呼出になり「スタックオーバーフロー」が発生します。
お客様の真の要望を満たす
「Override2.aspx」の修正によって、「ジャブ」「ストレート」「フック」「アッパー」「昇龍拳」が表示されるようになりました。この状態にカスタマイズできれば、バージョンアップ前の「common.js」の状態でも、「ジャブ」「昇龍拳」が表示され、お客様の要望をきちんと満たしています。
今回のカスタマイズの例では、「Punch」メソッド自体の内容が数行と少なく、「basePunch」に保存しておくメリットがあまり感じられませんでした。しかし、「jQuery」や「prototype.js」等のライブラリを使用していて、ライブラリの動作を変えたい場合があったとします。ライブラリの内容を、直接変更してしまうのは大変危険です。その場合は、「basePunch」のように「標準のメソッド」を保存しておく方法によって、大きなメリットが生まれてきます。
ありがとうございました
さて、今回でついに「TypePadクエスト」が終了です。「TypePadの自動修正」に対抗する目的で始まったこの企画。色々な試みをさせていただきました。第1話は「お馬鹿アプリ」、第2話は「Youtube + コナミコマンド」。そして今回は「TypePadの自動修正に対抗する」……。
そもそも「TypePadの自動修正」が発生する直接的な原因は「FireFox」でした。「FireFox」を使用しTypePadエディタ上で、画面スクロールを発生させることによって「半角スペースの統合」が発生します。半角スペースの数が狂うことによって、ソースコードが崩れるのです。しかし、記事の確認はエンジニアライフの運営上、必要不可欠である工程です。そこで、何とか解決する手段はないものかと調査を行い、今回の実装となりました。ソースコードは、うまく表示されているでしょうか?