5

プロジェクトでは、正規表現を使用してさまざまな種類の文を区別し、これらの文を処理する関数にマップする実験を行っています。

これらの文処理関数のほとんどは、正規表現のキャプチャ グループによって解析された文自体から引数を取ります。

例: 「私は 2 つの Cookie に対して $20 を支払いました」は、解析ツリー (辞書) の正規表現の 1 つと一致します。この正規表現は、extract $20 をグループ "price" として、2 をグループ "amount" として一致します。現在、正しい Handler 関数にマッピングし、次のように呼び出しています。

foreach(KeyValuePair<Regex, Type> pair in sentenceTypes)
{
    Match match = pair.Key.Match(text);
    if(match.Success)
    {
        IHandler handler = handlerFactory.CreateHandler(pair.Value);
        output = handler.Handle(match);
    }
}

単純なハンドラー クラスの例。

public class NoteCookiePriceHandler
    {
        public string Handle(Match match)
        {
            double payment = Convert.ToDouble(match.Result("${payment}"));
            int amount = Convert.ToInt32(match.Result("${amount}"));

            double price = payment / amount;
            return "The price is $" + price;
        }
    }

Moq を使用していくつかの単体テストをセットアップしようとしていたときに、Match オブジェクトや Regex を実際にモックできないことに気付きました。もっと考えてみると、名前付きグループが正しく解析され、適切なインターフェースなしで Handler クラスに渡されることに依存しているため、一般的に設計に多少の欠陥があるようです。

Match オブジェクトの受け渡しには問題があるように思われるため、マッピングされたハンドラ関数/クラスにパラメータを正しく渡す際に使用する、より効果的な設計に関する提案を探しています。

それができない場合は、RegexまたはMatchを効果的にモックする方法を見つけ出すための助けがあれば幸いです.少なくとも私の短期的な問題を解決するのに役立ちます. どちらもデフォルトのコンストラクターがないため、Moq でそれらのオブジェクトを作成するのに苦労しています。

編集:(Moqできない)一致オブジェクト自体ではなく、一致グループに文字列の辞書を渡すことで、少なくともモックの問題を解決することになりました。私はこの解決策に特に満足しているわけではないので、推奨事項を引き続き歓迎します。

foreach(KeyValuePair<Regex, Type> pair in sentenceTypes)
        {
            match = pair.Key.Match(text);
            if(match.Success)
            {
                IHandler handler= handlerFactory.CreateHandler(pair.Value);
                foreach (string groupName in pair.Key.GetGroupNames())
                {
                    matchGroups.Add(groupName, match.Groups[groupName].Value);
                }
                interpretation = handler.Handle(matchGroups);
4

1 に答える 1

1

悪い設計を回避する 1 つの方法は、単に解決したい問題ではなく、良い設計の原則から始めることです。これが、テスト駆動開発がコードの品質を変えるのに非常に強力である理由の 1 つです。この考え方は、「契約による設計」という名前で、TDD のずっと前から存在していました。デモをさせてください:

理想的なハンドラーをどのように見せたいですか? これはどう:

interface IHandler {
    String handle();
}

実装:

public class NoteCookiePriceHandler : IHandler
{  
    private double payment;
    private int amount;

    public NoteCookiePriceHandler(double payment, int amount) {
        this.payment = payment;
        this.amount = amount;
    }

    public String handle() {
        return "The price is $" + payment / amount;
    }
}

この理想的な設計から始めて、おそらくこの設計のテストから始めます。ハンドラーに送信するセンテンスのセンテンス入力を取得するにはどうすればよいでしょうか? さて、コンピュータ サイエンスのすべての問題は、間接的な別のレイヤーで解決できます。文パーサーがハンドラーを直接作成するのではなく、ファクトリーを使用してハンドラーを作成するとします。

interface HandlerFactory<T> where T: IHandler  {
    T getHandler(KeyValuePair<String, String> captureGroups);
}

その後、ハンドラごとに 1 つのファクトリを作成できますが、すぐにジェネリック ファクトリを作成する方法が見つかります。たとえば、リフレクションを使用すると、キャプチャ グループ名をコンストラクター パラメーターに一致させることができます。コンストラクター パラメーターのデータ型に基づいて、ジェネリック ハンドラー ファクトリに文字列を正しいデータ型に自動的に変換させることができます。これはすべて、いくつかの偽のハンドラーを作成し、いくつかのキーと値のペアの文字列入力を使用してそれらを設定するようにファクトリに要求することで、簡単にテストできます。

于 2013-08-10T20:10:05.370 に答える