読者です 読者をやめる 読者になる 読者になる

ビューティフルWebコード

美しいWebサイトのコーディングについて説明をしていきます。

URL設計 URLを管理するクラスを作ろう!

仕様変更が悪いのではない。仕様変更に絶えられない実装が悪いのだ!

プロジェクトは仕様変更との戦いです。仕様が変わったせいで、全てのコードを見直し、テストを全てやりなおすなんて日常茶飯事です。仕様が変わった事を恨んでも仕方がないので、仕様が変わってもすぐに対応できる準備をあらかじめしておく必要が有ります。

Web開発でよくあるのが、HTMLやControllerのソースコードにURLが直接記述されているので、URL変更が思うようにできない、という悲鳴です。

じゃあ直接記述しなければいいじゃん!

URLを管理するクラスを作りましょう!

URLを管理するクラスを作って、URLの処理を閉じ込めます。
パラメータ無しのURLであれば public const で公開すれば良いでしょう。
パラメータ有りの場合は、メソッドにしてパラメータを引数で渡せるようにしましょう。
パラメータが複雑であれば、パラメータのクラスを作ってそのクラスを引数で渡すようにしましょう。
URLエンコーディングや、暗号化されたパラメータなど、パラメータ生成ロジックが発生するのであれば、ロジックもメソッドに閉じ込めましょう。

//URL管理クラス
public class Urls 
{
        //エラー画面のurl 生成 
        //ErrorCodeはenumや定数にしておき、
        //わざわざエラーコード一覧の資料を見なくても実装できるようにしてあげましょう。
        public static string Error( ErrorCode ecd )
        {
            return string.Format( "/error?ecd={0}", ecd )
        }
        
        //ユーザ画面のURL 
        // idはintなので型指定してあげましょう
        public static string User( int id )
        {
            return string.Format( "/user?id={0}", id )
        }

        //認証用のURL tokenが必須なので、token生成やエンコード処理を閉じ込めている
        public static string Auth()
        {
              var encodedToken = UrlEncode( AuthToken.CreateToken() );
              return string.Format( "/auth?token={0}", encodedToken );
        }
}
  • エラー画面URLであれば、エラーコードがenumになっており、enumにコメントをつけてあれば、どういうエラーなのかを資料を見なくても実装できます。
  • ユーザ画面URLにいては、型がintのパラメータが必要ということがわかります。
  • 認証URLについては、複雑なロジックが内部に含まれていますが、ロジックが閉じ込められているため、利用者はロジックを意識することなく、呼び出すだけでURL生成ができます。

URL管理クラスアンチパターン

以下のコードはどうでしょう?

//アンチパターン
public class Urls 
{
        //エラー画面のurl フォーマット
        public const  string ErrorFormat = "/error?error_code={0}";
        
        //ユーザのTOP画面のフォーマット
        public const  string UserFormat = "/user?id={0}";

        //認証URLのフォーマット tokenを生成してURLエンコードをしてセットしてください。
        public const string AuthFormat = "/auth?token={0}";
}

これはパラメータ付きURLのフォーマットを返しています。
これではせっかくURL管理クラスを作る意味がほとんどありません。

  • すべてのURLについて利用する側でFormat変換をしなくてはいけません。
  • エラーコードを指定する場合、エラーコードの種類がわかりません。別の資料を見ながら手入力するのでしょうか。
  • userIdに関しては、idはintなのか、stringなのかわかりません。(よく使われるURLなんだから仕様理解しろよ!というツッコミもあるかもしれない)
  • 認証URLに関しては tokenってどうやって生成するのかわからない場合、知ってる人に質問しないといけません。

知ってる人が誰なのか?最悪たらい回しにされます。無駄な時間が過ぎます。
プライドが高い人は、token生成アルゴリズムが記載された資料を見つけて、自前で実装してしまうかもしれません。

  • URLエンコード忘れてしまった場合、テストで認証エラーが起きて原因調査に時間が取られます。

すべてに言えますが、URL組み立てロジックがいたるところに分散してしまい、無駄なソースコードが増えます。
stringのformatは便利ですが、パラメータ数が一致しないときには例外が発生するなど、色々面倒になります。
ControllerやViewでformatを組み立てると、そのコードが実行されないと、バグがわかりません。

正しいURL管理クラス設計を!

たったひとつ、URLを管理するクラスを正しく設計し、共有するだけで、URLに関する無駄な結合テスト単体テストレベルで可能になります。
URL組み立てロジックをそれぞれ実装しなくて済みます。
複雑なURL組み立てをそれぞれの開発者が意識しなくてよくなります。

URLを管理するクラスが肥大化してきたよ!

よくあります。

classを階層化するなり、Bridgeパターンを適用するなり、細分化してください。
クラスのパスが変わってもビルドエラー箇所を直せば済みます。
結合テストを書いてるのであれば、結合テストを実行してあげてください。それだけで済みます。

まとめ

  • URL管理クラスを作り、パラメータなどのロジックはこのクラスに閉じ込める。stringフォーマットをpublicで公開しない。
  • URL管理クラスの単体テストをすれば、URL生成ロジックのテストは完了する。
  • 肥大化してきたら、細分化して、メンテナンスしやすいようにする。