やってみる

アウトプットすべく己を導くためのブログ。その試行錯誤すらたれ流す。

C#プロジェクトにNullable要素を追加するツール(再帰版。リファクタリング)

 Commandパターンでリファクタリングした。

成果物

残念なコード

 前回、コードがとても見づらくなってしまった。

class Program
{
    static void Main(string[] args)
    {
        if (ShowHelp(args)) return;
        foreach (string path in GetProjectFiles(args)) {
            var xml = CreateNullableXDocument(path);
            using (var writer = CreateOmitXmlDeclarationWriter(path)) { xml.Save(writer); }
        }
    }
    static bool ShowHelp(string[] args)
    {
        if (0 < args.Length && "-r" != args[0]) {
            Console.WriteLine(@".csprojファイルに<Nullable>enable</Nullable>を設定する。<Nullable>が既存なら何もしない。引数-rで再帰的に処理する。");
            return true;
        } else { return false; }
    }
    ...
}

 どこが問題か。終了するパターンが1つでなく2通りになったことで条件分岐が必要になってしまった。そのせいで複雑化した。

 終了パターンは以下2通りである。

  • ヘルプ表示して終了する
  • <Nullable>挿入して終了する

 これを「ヘルプ表示する」と「<Nullable>挿入する」という2つのコマンドとして捉えることができる。コマンドをインタフェースとして共通化すれば呼出元がスッキリ書けるはず。

リファクタリング

 ヘルプと<Nullable>挿入処理を共通インタフェースにして、別クラス内で処理を実装する。

interface ICommand
{
    public void Run(string[] args);
}
class ShowHelpCommand : ICommand
{
    public void Run(string[] args)
    {
        // ヘルプを表示する処理
    }
}
class SetupNullableCommand : ICommand
{
    public void Run(string[] args)
    {
        // <Nullable>enable</Nullable>を挿入する処理
    }
}

 これで以下のように呼出元がスッキリした。すばらしい。

class Program
{
    static void Main(string[] args)
    {
        ICommand cmd = Parse(args);
        cmd.Run(args);
    }
    static ICommand Parse(string[] args)
    {
        if (0 < args.Length && "-r" != args[0]) { return new ShowHelpCommand(); }
        else { return new SetupNullableCommand(); }
    }
}

 1回の実行あたり1つのコマンドを1回実行する構造。コマンドが処理する内容は、各コマンドクラスで実装する。

所感

 いい感じ。

対象環境

$ uname -a
Linux raspberrypi 4.19.42-v7+ #1218 SMP Tue May 14 00:48:17 BST 2019 armv7l GNU/Linux