任务设计模式与条件

时间:2016-07-29 14:10:03

标签: java scala design-patterns

所有代码段都在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)
}

因此,考虑到在执行测试时定义了所有后置条件,这是最佳设计模式吗?

非常感谢!

1 个答案:

答案 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应该不是一个大问题。