2006 年 7 月 6 日 19 時 30 分

役割分担と実装の分割


このアーカイブは同期化されません。 mixi の日記が更新されても、このアーカイブには反映されません。


これで、通常のショートカットと、
インターネットショートカットの 2 つを読めるようになった。

LinkTargetColumn に実装してテストと行きたいところだが、
少しコードが増えてきたので、
カラムの実装の前に、リファクタリングを行おう。

クラスの設計というのは、基本的には難しい。
技術的に難しいということではなく、
考え方次第ではいくつも答えがあることが難しいのだ。
クイズのように特定の正解が存在するわけではない。

俺は、オブジェクト的な考え方をするのは苦手なほうだ。
なので、最初からクラスとして設計するのではなく、
まずはとりあえず実装してから、
後で徐々に分割していく手法をよく使う。

今日は LinkTargetColumn の実装分割をやろう。

InternetShortcutClass を使えば、リンク先の取得はできる。
しかし、それをそのまま LinkTargetColumn に入れると、
コードが長くなり、読みにくくなってくる。

    public override object GetValue(ref SHCOLUMNDATA pscd) {
        (…省略…)

        switch (pscd.pwszExt.ToLower()) {

        case ".lnk":
            (…lnk ファイルを読む長いコード…)
            return ~;

        case ".url":
            (…url ファイルを読む長いコード…)
            return ~;

        default:
            throw new ComFalseException();

        }

このようにメソッド内に大きな switch ができてしまうのだ。
こういう場面に遭遇したら、それは実装分割のチャンスだ。
あくまでもチャンスなので、あらゆる switch を
分割しなければならないわけではないが、
殆どの場合、それはいい機会となる。

まず、最初に思いつくのが、ショートカット先の取得を
ヘルパメソッドとして分割することだ。
メソッドとして分けるだけでも見通しが良くなる。

分割は、以下のように考えて行う。

・switch が行う目的を単純にし
 各 case で行う処理を整理する。
 単純化できない場合は、複数の switch に分ける。

 ⇒ファイルを読んで、リンク先を得るってのが目的だ。

・switch ごと他のメソッドに引っ越すのではなく、
 switch の各 case 内を、個別のメソッドに移動する。
 メソッドのシグネチャはばらばらでもかまわない。

 ⇒ファイルごとに扱いが異なるので、
  拡張子ごとにわけるのが自然だ。

・可能であれば、default も分割する。

 ⇒対応していないファイル形式の場合、
  ただ単に例外を発生させるメソッドを作る。

・分割されたメソッドが、フィールドに依存する場合、
 メンバメソッドとして定義する代わりに、
 static メソッドとしてフィールドを引数として渡す。 

 ⇒今回はフィールドに依存することはない。

こういう風に分割する。まあ役割分担だ。
こうして分割したメソッドは、独立性の高い関数となる。

完全に引数にしか依存しない独立性の高い関数は、
別のクラスに移動することもできるのだ。
いわば、グローバル関数のような位置づけにする。
まあ、C# では、グローバル関数は認められないけどね。

    public override object GetValue(ref SHCOLUMNDATA pscd) {
        (…省略…)

        switch (pscd.pwszExt.ToLower()) {

        case ".lnk":
            return GetShortcutTarget(pscd.wszFile);

        case ".url":
            return GetInternetShortcutTarget(pscd.wszFile);

        default:
            return GetOtherTarget();

        }

    }

    static private string GetShortcutTarget(string path) {
        (…省略…)
    }
    static private string GetInternetShortcutTarget(string path) {
        (…省略…)
    }
    static private string GetOtherTarget() {
        throw new ComFalseException();
    }

まず、これが一段階だ。

実は、別にこれだけでもいいのだ。
見通しが良くなっているだけでも十分である。



Copyright (c) 1994-2007 Project Loafer. All rights reserved.