2016-07-29 8 views
2

スニペットコードはすべてjavaになりますが、私のコードはscalaで実装されるため、いくつかのソリューションではその言語の利点を利用できます。条件付きタスク設計パターン

いくつかのコードを実行した後に取得するいくつかのデータに対していくつかの事後条件を実行しようとしており、使用するのに最適なパターンを知りたいと思っています。

このコマンドを実行すると、パラメータ(これはjavaです)によって特定の情報を渡すことができます。

-DallTheSame=true -DconcreteExpectedResultPerUnit=3,1,1 -DminimumThreshold=60 

次に、今後どのように拡張できるかという問題を解決するための最良の方法を検討し始めました。

1. Visitorパターン

パラメータのすべては、すべての訪問を実施し、チェックを実行するための時間が来て、その後、同じオブジェクトを訪問します関連するクラス

class AllTheSameCheck 
class ConcreteExpectedResultPerUnitCheck 
class MinimumThresholdCheck 

を持っていますそれらのすべて、必要に応じて、チェック

class AllTheSameCheck { 
    public void visit(Object object) { 
     if(isAllTheSameDefined()){ 
      // do the check 
     } 
    } 
} 

と他人のために同じコードを実行します。

2. Decoratorパターン

前のクラス

class BasicCheck 
class AllTheSameDecoratorCheck extends BasicCheck 
class ConcreteExpectedResultPerUnitDecoratorCheck extends BasicCheck 
class MinimumThresholdDecoratorCheck extends BasicCheck 

の観点と同じですが、メインの実行コードで、彼らがする必要がありますので、それは、あたりとしては見デコレータではありません条件文

メインのコードで定義された:

BasicCheck basicCheck = new BasicCheck() 
if(isAllTheSameDefined()){ 
    basicCheck = new AllTheSameDefined(basicCheck) 
} 
if(isConcreteExpected...Defined()){ 
    basicCheck = new ConcreteExpected....(basicCheck) 
} 

したがって、テストを実行するときにすべての事後条件が定義されていることを考慮すると、その場合の最適な設計パターンはどれですか?

ありがとうございます!

+1

あなたの意図によって異なります。私の意見では、Visitorは十分にエレガントではなく、オブジェクト間の複雑なやりとりが必要なときに問題に適用される方が優れています。デコレータはシンプルでスケーラブルで、各アクションの前後に新しいステップを簡単に追加できます。ただし、実行順序に制約があります(デコレータの実行順序が重要な場合はこのコードを維持するのが難しい)。パラメータ間のやりとり。 – NikolayKondratyev

+0

実行順序は問題ではありません。不変な結果に対して実行されるチェックの問題です。 – RamonBoza

+0

したがって、私はデコレータ、シンプルで明白なソリューションを好むだろう。しかし、それを簡単にすることもできます。チェックとコールを実行するクラスの 'List'を作成します(結果は引数として渡されます)。かなりシンプルですが、依然としてすべての要件を満たしていると思いませんか? – NikolayKondratyev

答えて

3

特定のプロパティが存在する場合に実行する必要がある特定の戦略については、明らかにstrategy patternを使用しないでください。戦略は唯一のいくつかのロジックを実行する必要がありますが、彼らは単にものために求められることができるシングルトンに登録することができます任意の状態を保持するべきではないとして

public class AllTheSameCheck implements CheckStrategy { 
    @Override 
    public String getName() { 
     return "allTheSame"; 
    } 

    @Override 
    public boolean validate(Object toValidate, String arguments) { 
     ... 
    } 
} 

public interface CheckStrategy { 
    String getName(); 
    boolean validate(Object toValidate, String arguments); 
} 

具体的な実装は、今のようなものかもしれません妥当性検査の管理を担当する。一致するすべての戦略が実行されますPostValidation.INSTANCE.check(someObject, somePropertiesMap);を呼んで

public enum PostValidation { 
    INSTANCE; 

    private Map<String, CheckStrategy> registeredStrategies = new HashMap<>(); 

    public void registerNewStrategy(String propertyName, CheckStrategy strategy) { 
     registeredStrategies.put(propertyName, strategy); 
    } 

    public boolean check(Object toValidate, Properties properties) { 
     boolean checkSucceeded = true; 
     for (String key : properties.stringPropertyNames()) { 
      CheckStrategy strategy = registeredStrategies.get(key); 
      if (null != strategy) { 
       checkSucceeded = stategy.validate(toValidate, properties.getProperty(key)); 
      } 
      if (!checkSucceeded) { 
       LOG.warn(strategy.getClass().getSimpleName() + " failed validation"); 
       break; 
      } 
     } 
     return checkSucceeded; 
    } 
} 

。 1つの検証が失敗した場合、検証を続行することはあまり意味がないため、残りの検証はスキップされますが、この動作を変更することは非常に簡単です。

check(...)操作が呼び出される前に、利用可能な検証方法はすべて一度登録する必要があります。これは、必要に応じてプラグインメカニズムを介してランタイムに新しい戦略を動的に追加することも可能にしますが、戦略が耳を傾けるべき名前は(システム)プロパティ内で設定する必要があります。

システムプロパティ(-Darg=val)として情報を渡すと、これらのプロパティはそのままcheck(...)メソッドに渡されました。しかし、このプロパティには実際に必要なデータがたくさん含まれています。つまり、オペレーティングシステムの名前やバージョン、クラスパスなどの特定の実行時環境データ...システムプロパティのデータは、checkメソッドでは大量には必要ありません。アプリケーションの引数としてのそれらはおそらくよりクリーンです。アプリケーションでは、あなたは、このようなプロパティオブジェクトにそれらを格納することができ始める:プロパティ内に格納されている実際の値が文字列の性質のものであるとして

public static void main(String ... args) { 
    // Register the strategies 
    CheckStrategy strat1 = new AllTheSameCheck(); 
    CheckStrategy strat2 = new ConcreteExpectedResultPerUnitCheck(); 
    CheckStrategy strat3 = new MinimumThresholdDecoratorCheck(); 

    PostValidation.INSTANCE.registerNewStrategy(strat1.getName(), strat1); 
    PostValidation.INSTANCE.registerNewStrategy(strat2.getName(), strat2); 
    PostValidation.INSTANCE.registerNewStrategy(strat3.getName(), strat3); 
    ... 
    // Parse the arguments 
    Properties prop = new Properties(); 
    for (String arg : args) { 
     String[] kv = arg.split("="); 
     if (kv.length == 2) { 
      prop.setProperty(kv[0], kv[1]); 
     } 
    } 
    // Pass the passed arguments to the application 
    App app = new App(prop); 
    ... 
} 

、彼らは明らかにvalidate(...)メソッド内の各Javaの型に変換する必要があります。 Java型に文字列を変換するためのヒューリスティックは、このようになります

public class BaseType<T> { 
    public T value; 
    public Class<T> type; 

    public BaseType(T value, Class<T> type) { 
     this.value = value; 
     this.type = type; 
    } 

    public T getValue() { 
     return this.value; 
    } 

    public Class<T> getType() { 
     return this.type; 
    } 

    public static BaseType<?> getBaseType(String string) { 
     if (string == null) { 
      throw new IllegalArgumentException("The provided string must not be null"); 
     } 

     if ("true".equals(string) || "false".equals(string)) { 
      return new BaseType<>(Boolean.getBoolean(string), Boolean.class); 
     } 
     else if (string.startsWith("'")) { 
      return new BaseType<>(string, String.class); 
     } 
     else if (string.contains(",")) { 
      List<BaseType<?>> baseTypes = new ArrayList<>(); 
      String[] values = string.split(","); 
      for (String value : values) { 
       baseTypes.add(getBaseType(value)); 
      } 
      return new BaseType<>(baseTypes, List.class); 
     } 
     else if (string.contains(".")) { 
      return new BaseType<>(Float.parseFloat(string), Float.class); 
     } 
     else { 
      return new BaseType<>(Integer.parseInt(string), Integer.class); 
     } 
    } 
} 

getBaseType(String)文字列が表すことができるタイプを決定するための簡単なヒューリスティックを有するファクトリメソッドです。あなたのニーズは分かりませんし、実際の問題の説明が少し限られているため、あなたのニーズに合わせてBaseTypeクラスを調整する必要があります。 AllTheSameCheck validateメソッド内

あなたは今、このように上から提案BaseTypeクラスを使用して、渡された文字列引数の値に変換することができます:

public boolean validate(Object toValidate, String arguments) { 
    boolean value = false; 
    BaseType<?> baseType = BaseType.getBaseType(arguments); 
    if (Boolean.class.equals(baseType.getType)) { 
     value = baseType.getValue(); 
    } 
    ... 
} 

渡されたint型の一連の値を取得しますConcreteExpectedResultPerUnitCheck、一方で、見えるかもしれませんこのように:あなたは将来の使用のための拡張可能なソリューションを求めたよう

public boolean validate(Object toValidate, String arguments) { 
    List<Integer> values = new ArrayList<>(); 
    BaseType<?> baseType = BaseType.getBaseType(arguments); 
    if (List.class.equals(baseType.getType)) { 
     List<BaseType<?>> listType = (List<BaseType<?>>)baseType.getValue(); 
     for (BaseTyep<?> base : listType) { 
      if (Integer.class.equals(base.getType())) { 
       values.add(base.getValue()); 
      } 
     } 
    } 
    ... 
} 

、提案された戦略パターンは、独自のクラスに実際の実装を分離します。新しいクラスをその場で追加することができる場合は、i。 e。プラグインメカニズムを提供することで、新しい戦略を含むJARをアプリケーションに追加し、含まれた戦略をPostValidationシングルトンで登録し、戦略がそのプロパティにトリガーする名前を追加することができます。

戦略が複数の入力を必要とする場合、私が現在見ている将来のニーズに対する唯一の欠点は、ここではおそらく、それぞれの引数値を含む文字列ではなく、全体のparametersオブジェクトを戦略に渡すべきでしょう。

私はまだScalaでプログラミングしていないので、私は普通のJavaでアイディアを保っていました。しかし、Scalaにコードを変換することは、私が推測する大きな問題ではありません。