特定のプロパティが存在する場合に実行する必要がある特定の戦略については、明らかに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にコードを変換することは、私が推測する大きな問題ではありません。
あなたの意図によって異なります。私の意見では、Visitorは十分にエレガントではなく、オブジェクト間の複雑なやりとりが必要なときに問題に適用される方が優れています。デコレータはシンプルでスケーラブルで、各アクションの前後に新しいステップを簡単に追加できます。ただし、実行順序に制約があります(デコレータの実行順序が重要な場合はこのコードを維持するのが難しい)。パラメータ間のやりとり。 – NikolayKondratyev
実行順序は問題ではありません。不変な結果に対して実行されるチェックの問題です。 – RamonBoza
したがって、私はデコレータ、シンプルで明白なソリューションを好むだろう。しかし、それを簡単にすることもできます。チェックとコールを実行するクラスの 'List'を作成します(結果は引数として渡されます)。かなりシンプルですが、依然としてすべての要件を満たしていると思いませんか? – NikolayKondratyev