4

職場で、外部ファイル (Excel シート、テキスト ファイルなど) から値を取得し、それらの値を別のシステムに供給される複雑な命令に変換するこのアプリケーションを実行しています。

以下のコードは少し単純化されています (命令がなく、ロジックが非常に単純です) が、考え方は同じです。私には、さまざまな種類のビジネス ロジックを背後で実行する約 60 人の異なる翻訳者がいます。実行する引数を 1 つだけ取るものもあります。他のものは複数の引数を取ります。

抽象翻訳クラスがあります。クラスのユーザーは、2 つのパブリック メソッドを使用します。Translate を使用して翻訳ロジックを実行し、CanTranslate を使用すると、トランスレータが開始する準備ができているかどうかを確認できます。

この抽象クラスを使用する開発者は、実際のビジネス ロジックを含む DoTranslate メソッドを実装する必要があります。デフォルトでは、CanTranslate は常に true を返しますが、検証が必要な場合はオーバーライドできます。

抽象トランスレータの基本クラスは次のとおりです。

// Contains some base logic which is the same for all translators
public abstract class BaseTranslator
{
    // Public translate method
    public void Translate()
    {
        if (CanTranslate())
            DoTranslate();
    }

    // Checks if we are ready to translate
    // True by default
    public virtual bool CanTranslate()
    {
        return true;
    }

    // This method is used to implement business logic
    public abstract void DoTranslate();
}

具体的なトランスレータ クラスの実装は次のとおりです。

// Translates beer names
public class ReverseTranslator : BaseTranslator
{
    // Use of properties to allow strongly typed arguments
    // which can be seen by the developer at design time
    public string BeerName { get; set; }

    // Validation
    public override bool CanTranslate()
    {
        if (BeerName.Equals("Budweiser") || BeerName.Equals("Stella"))
            return true;
        else
            return false;
    }

    // Implementation of the business logic
    public override void DoTranslate()
    {
        char[] letters = BeerName.ToCharArray();
        Array.Reverse(letters);
        Console.WriteLine(new string(letters));
    }
}

そして、使用中はこんな感じです。

class Program
{
    public static void Main(string[] args)
    {
        var translator = new ReverseTranslator();

        translator.BeerName = "Stella";
        translator.Translate();

        translator.BeerName = "I'm not a beer";
        // This line will not translate since it's not a valid beer name.
        translator.Translate();

        Console.ReadLine();
    }
}

プロの:

  • 特定のビジネス ロジックを保守可能な小さな単位に分離する
  • 翻訳者は、アプリケーションの他の部分で簡単に再利用できます
  • 翻訳者は簡単に単体テストできます
  • プロパティにより、トランスレータのユーザーはどの引数が必要かを確認できます

私の問題:

  • さまざまなコントローラ クラスが多くのトランスレータを使用しています。カップリングが多すぎる。

トランスレータの作成に Factory パターンを使用することを考えましたが、設計時にプロパティを引数のヒントとして使用できません。

したがって、基本的には、設計時に必要な引数を簡単に確認できるソリューションを探しています。同時に、各コントローラーに 30 個の新しい xTranslator ステートメントを持たせないことで結合を減らしたいと考えています。

PS: このコードには .NET 3.5 しか使用できません。

4

3 に答える 3

2
different controller classes are using many translators. I have too much coupling

コントローラ クラスは、抽象化のみに依存する必要がありBaseTranslatorます。したがって、結合はあまりなく、実際には疎結合のコードになります。依存性注入を介して (たとえば、コンストラクター パラメーターを介して) コントローラーに依存性を注入します。

コードを基本型のみに依存させるオプションの 1 つ -Text基本クラスに文字列プロパティを作成します。

BaseTranslator translator = new ReverseTranslator(); // inject implementation
translator.Text = "Stella";
translator.Translate();
translator.Text = "I'm not a beer";
translator.Translate();
于 2012-11-27T21:54:25.543 に答える
0

具象クラスの特定のパラメーターの設計時情報を取得するには、 具象クラスのインスタンスを操作する必要があります
コントローラの結合を減らすには: 抽象クラスのインスタンスを制限する必要があります
両方を同時に同じ場所に置くことはできません。

全体的な設計を作り直すことで、結合を排除し、設計時の情報の必要性をなくすことができます。

トランスレータの作成と初期化をコントローラからファクトリまたはIoCコンテナに移動します。このコンテナは、外部ファイルからデータの行を入力として受け取ります(必要に応じて、処理可能な形式にマッサージされます)。

引数のコレクションを受け取るコンストラクターをトランスレーターに要求させます。
利点:

  • 具象クラス自体だけが、それ自体またはそのパラメーターに関する詳細を知る必要があります。
  • 工場では、どの翻訳者をどの状況で使用するかについての情報が必要です。

class TranslatorFactory
{
    //translator lookup table
    private Dictionary<string, Func<List<string>,BaseTranslator>> Translators = 
        new Dictionary<string,Func<List<string>,BaseTranslator>>{
            {"Reverse", (args)=>new ReverseTranslator(args)},
            {"Explode", (args)=>new ExplodeTranslator(args)}        };

    public BaseTranslator GetTranslatorForRow(string command, List<string> arguments)
    {
        if(Translators.ContainsKey(command) )
        {
             return Translators[command](arguments);
        }
        return null; //or default, or throw exception
    }
}


abstract class BaseTranslator
{
    ...
    public BaseTranslator(List<string> args)
    {
    }
}

class ReverseTranslator: BaseTranslator
{
    public string BeerName {get;set;}
    public ReverseTranslator(List<string> args)
    {
        BeerName = args[0];
    }
}

さらに進んで、属性とリフレクションを使用してファクトリと具象クラス間の結合を削除し、ルックアップテーブルを動的に構築できます。

[TranslatorFor("Reverse")]
class ReverseTranslator: BaseTranslator
{
    ...
}
于 2012-11-28T00:09:44.740 に答える
0

私には、次の点について矛盾することがあります。

  • 一方では、Translatorの作成者は、独自の具象クラスを定義できます(そして、具象クラスが正しく機能するための値が必要な新しい「必須」プロパティを追加できます)。
  • 一方、クラスをインスタンス化し、正しいパラメーターをクラスに渡すのはあなたであるため、コード内で他のユーザーによって定義された新しい具象クラスに依存します。

これが正しければ、モデルに適切なレベルの抽象化が見つからないと思います。言い換えると、現在の抽象化は、各具象クラスで拡張されているため、(多かれ少なかれ)まったく役に立ちません。したがって、私の提案は、最初にモデル自体を調べることです(ただし、モデルがわからないため、これは単なる推測です:-))。

ここにいる誰かが、たとえば7〜10の代表的な具体的なクラス名と、ドメインをよりよく理解できるようにするために期待される入力値のリストを提供できれば、おそらくあなたをよりよく導くことができるでしょう。

于 2012-11-27T22:17:07.833 に答える