所有代码段都在java中,而我的代码将在scala中实现,所以也许某些解决方案可以利用该语言的优点。
我正在尝试对执行某些代码后得到的一些数据执行一些后置条件,并且想知道要使用的最佳模式。
让我们看一下,在执行命令时,我可以通过参数(这是java)传递某些信息。
-DallTheSame=true -DconcreteExpectedResultPerUnit=3,1,1 -DminimumThreshold=60
然后我开始研究解决如何在未来扩展它的问题的最佳方法。
1。访客模式
所有参数都有一个关联的类
class AllTheSameCheck
class ConcreteExpectedResultPerUnitCheck
class MinimumThresholdCheck
所有人都将实施访问,然后执行检查的时间到来,同一个对象将访问所有这些,如果需要,将执行检查
class AllTheSameCheck {
public void visit(Object object) {
if(isAllTheSameDefined()){
// do the check
}
}
}
和其他人的代码相同。
2。装饰者模式
在类
方面与以前相同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)
}
因此,考虑到在执行测试时定义了所有后置条件,这是最佳设计模式吗?
非常感谢!
答案 0 :(得分:3)
由于您的检查显然是某些属性存在时应执行的某些策略,为什么不使用strategy pattern?
public interface CheckStrategy {
String getName();
boolean validate(Object toValidate, String arguments);
}
具体实现现在可以是:
public class AllTheSameCheck implements CheckStrategy {
@Override
public String getName() {
return "allTheSame";
}
@Override
public boolean validate(Object toValidate, String arguments) {
...
}
}
由于策略应该只执行某些逻辑,但不应该保持任何状态,因此可以简单地使用单例注册,也可以要求它们处理验证检查的管理。
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;
}
}
在调用PostValidation.INSTANCE.check(someObject, somePropertiesMap);
时,将执行所有匹配的策略。如果一个验证失败,则跳过其余验证,因为验证没有多大意义,但是改变这种行为很容易。
在调用check(...)
操作之前,应该注册所有可用的验证策略。如果需要,这还允许通过插件机制在运行时动态添加新策略,尽管策略应该监听的名称也需要在(系统)属性中设置。
当您将信息作为系统属性(-Darg=val
)传递时,我将这些属性原样传递给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
验证方法中,您现在可以使用上面提议的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());
}
}
}
...
}
当您要求提供可扩展的解决方案以供将来使用时,建议的策略模式将实际实现与其自己的类分离。如果你能够动态添加新课程我。即通过提供插件机制,您只需将包含新策略的JAR添加到您的应用程序中,使用PostValidation
单例注册包含的策略,并将策略应该触发的名称添加到属性中。
我目前看到的未来需求的唯一缺点是,当策略需要多个输入时。在这里,您可能应该将整个参数对象传递给策略,而不是传递包含相应参数值的字符串。
由于我还没有在Scala中编程,我将我的想法保留在普通的Java中。但是,我想将代码转换为Scala应该不是一个大问题。