5

我正在编写一个 servlet,它将根据一些用户可定义的规则有条件地修改 HTTP 标头。(编辑:这些规则在启动时读取的 XML 文件中定义。)例如,如果响应标头不存在,则将“X-UA-Compatible: IE=edge,chrome=1”添加到响应标头中,并且如果请求指定了与已知模式匹配的“User-Agent”标头。没有更好的想法,我尝试制作自己的 POJO 来代表这些规则。它“有效”,但我觉得必须有一种更标准或更灵活的方式来做到这一点。

是否有可以解决此问题的通用库或工具(无论是内置的还是第三方的)?我听说过并读过一些关于“规则引擎”的内容,但它们似乎是更复杂/繁重的工具,不适用于像我这样简单的问题。

为了说明我正在尝试做的事情,我创建了一个简化的程序,该程序根据“条件”将“规则”应用于数字,例如“是偶数”。在这里,抱歉有点长。

主.java

package my.example;

import java.util.*;

import my.example.conditions.*;
import my.example.rules.*;

public class Main {
    
    public static void main(String args[]) {
        // Some sample objects to evaluate
        Collection<Integer> numbers = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8);
        print(numbers);
        
        // Define rules
        Collection<Rule<Integer>> rules = new ArrayList<Rule<Integer>>();
        rules.add(createRuleToMultiplyEvenNumbersBy4());
        rules.add(createRuleToAdd1ToEveryNumber());
        
        // Process the rules for each sample object
        Collection<Integer> newNumbers = new ArrayList<Integer>();
        for (Integer number : numbers) {
            Integer newNumber = number;
            for (Rule<Integer> rule : rules)
                newNumber = rule.apply(newNumber);
            newNumbers.add(newNumber);
        }

        print(newNumbers);
    }

    private static Rule<Integer> createRuleToMultiplyEvenNumbersBy4() {
        MultiplyNumberRule rule = new MultiplyNumberRule(true, 4);
        rule.addCondition(new NumberIsEvenCondition());
        return rule;
    }
    
    private static Rule<Integer> createRuleToAdd1ToEveryNumber() {
        AddNumberRule rule = new AddNumberRule(true, 1);
        rule.addCondition(new ConstantCondition<Integer>(true));
        return rule;
    }
    
    private static void print(Collection<Integer> numbers) {
        System.out.print("Numbers: ");
        for (Integer number : numbers) {
            System.out.print(number);
            System.out.print(" ");
        }
        System.out.print("\r\n");
    }   
    

}

条件.java

package my.example.conditions;

public interface Condition<T> {
    boolean appliesTo(T obj);
}

常量条件.java

package my.example.conditions;

public class ConstantCondition<T> implements Condition<T> {
    private boolean constant;
    
    public ConstantCondition(boolean alwaysReturnThisValue) {
        constant = alwaysReturnThisValue;
    }
    
    @Override
    public boolean appliesTo(T target) {
        return constant;
    }
}

NumberIsEvenCondition.java

package my.example.conditions;

public class NumberIsEvenCondition implements Condition<Integer> {
    @Override
    public boolean appliesTo(Integer i) {
        return (i % 2 == 0);
    }
}

规则.java

package my.example.rules;

public interface Rule<T> {
    T apply(T target);
}

抽象规则.java

package my.example.rules;

import java.util.*;

import my.example.conditions.Condition;
public abstract class AbstractRule<T> implements Rule<T> {
    private Collection<Condition<T>> conditions;
    private boolean requireAllConditions;
    
    public AbstractRule(boolean requireAllConditions) {
        conditions = new ArrayList<Condition<T>>();
        this.requireAllConditions = requireAllConditions;
    }

    public void addCondition(Condition<T> condition) {
        conditions.add(condition);
    }

    @Override
    public T apply(T target) {
        boolean isApplicable;
        if (requireAllConditions)
            isApplicable = allConditionsSatisfied(target);
        else
            isApplicable = atLeastOneConditionSatisfied(target);
        
        if (isApplicable)
            target = process(target);
        
        return target;
    }
    
    // Check if all conditions are met
    protected boolean allConditionsSatisfied(T target) {
        for (Condition<T> condition : conditions) {
            if (!condition.appliesTo(target))
                return false;
        }       
        return true;
    }
    
    // Check if any conditions are met
    protected boolean atLeastOneConditionSatisfied(T target) {
        for (Condition<T> condition : conditions) {
            if (condition.appliesTo(target))
                return true;
        }       
        return false;
    }

    abstract T process(T target);

}

AddNumberRule.java

package my.example.rules;

public class AddNumberRule extends AbstractRule<Integer> {
    private Integer addend;
    
    public AddNumberRule(boolean requireAllConditions) {
        this(requireAllConditions, 0);
    }
    
    public AddNumberRule(boolean requireAllConditions, Integer addend) {
        super(requireAllConditions);
        this.addend = addend;
    }
    
    @Override
    public Integer process(Integer i) {
        return i + addend;
    }
}

MultiplyNumberRule.java

package my.example.rules;

public class MultiplyNumberRule extends AbstractRule<Integer> {
    private Integer factor;
    
    public MultiplyNumberRule(boolean requireAllConditions) {
        this(requireAllConditions, 1);
    }
    
    public MultiplyNumberRule(boolean requireAllConditions, Integer factor) {
        super(requireAllConditions);
        this.factor = factor;
    }
    
    @Override
    public Integer process(Integer i) {
        return i * factor;
    }
}
4

2 回答 2

1

好吧,我会使用Commons Chain

一种用于组织复杂处理流程执行的流行技术是“责任链”模式,正如经典的“四人组”设计模式一书中所描述的(以及许多其他地方)。尽管实现此设计模式所需的基本 API 合约非常简单,但拥有一个有助于使用该模式的基础 API 是有用的,并且(更重要的是)鼓励组合来自多个不同来源的命令实现。

这是一种常见的设计模式,猜测适合您的问题

于 2013-08-15T20:48:50.250 回答
0

我已经修改了尝试使用Commons Chain的原始代码,但看起来并没有太大不同。Luiz E.,这大致是您的建议吗?似乎 commons-chain 不包含任何“条件”的概念——这是留给用户作为Command.

主.java

package my.example;

import java.util.*;

import org.apache.commons.chain.*;
import org.apache.commons.chain.impl.*;

import my.example.commands.*;
import my.example.conditions.*;

public class Main {

    private static final String NUMBERS = "numbers";

    public static void main(String args[]) throws Exception {
        // Some sample objects to evaluate
        Context context = new ContextBase();
        setNumbersInContext(context, Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8));
        printNumbersFromContext(context);

        // Define rules
        Chain ruleChain = new ChainBase();
        ruleChain.addCommand(new MultiplyNumberCommand(4, new NumberIsEvenCondition()));
        ruleChain.addCommand(new AddNumberCommand(1));

        // Process the rules
        ruleChain.execute(context);
        printNumbersFromContext(context);
    }

    private static void printNumbersFromContext(Context context) {
        Collection<Integer> numbers = getNumbersFromContext(context);
        System.out.print("Numbers: ");
        for (Integer number : numbers) {
            System.out.print(number);
            System.out.print(" ");
        }
        System.out.print("\r\n");
    }

    @SuppressWarnings("unchecked")
    public static Collection<Integer> getNumbersFromContext(Context context) {
        Object obj = context.get(NUMBERS);
        try {
            return (Collection<Integer>) obj;
        }
        catch (ClassCastException e) {
            throw new IllegalStateException("Context did not contain the required data. ClassCastException message is: " + e.getMessage());
        }
    }   

    @SuppressWarnings("unchecked")
    public static void setNumbersInContext(Context context, Collection<Integer> numbers) {
        context.put(NUMBERS, numbers);
    }   


}

AbstractNumberCommand.java

package my.example.commands;

import static my.example.Main.getNumbersFromContext;
import static my.example.Main.setNumbersInContext;

import java.util.ArrayList;
import java.util.Collection;

import my.example.conditions.Condition;

import org.apache.commons.chain.*;

public abstract class AbstractNumberCommand implements Command {

    private boolean continueProcessing = true;
    protected Condition<Integer> condition;

    protected AbstractNumberCommand(Condition<Integer> condition) {
        this.condition = condition;
    }

    public void continueProcessing(boolean continueProcessing) {
        this.continueProcessing = continueProcessing;
    }

    @Override
    public boolean execute(Context context) throws Exception {
        Collection<Integer> numbers = getNumbersFromContext(context);
        Collection<Integer> newNumbers = new ArrayList<Integer>();
        for (int number : numbers)
            if (condition.appliesTo(number))
                newNumbers.add(modifyNumber(number));
            else
                newNumbers.add(number);
        setNumbersInContext(context, newNumbers);

        if (continueProcessing)
            return CONTINUE_PROCESSING;
        else
            return PROCESSING_COMPLETE;
    }

    protected abstract int modifyNumber(int number);
}

AddNumberCommand.java

package my.example.commands;

import my.example.conditions.*;

public class AddNumberCommand extends AbstractNumberCommand {

    private int addend;

    public AddNumberCommand() {
        this(0);
    }

    public AddNumberCommand(int addend) {
        this(addend, new ConstantCondition<Integer>(true));
    }

    public AddNumberCommand(int addend, Condition<Integer> condition) {
        super(condition);
        this.addend = addend;
    }

    @Override
    protected int modifyNumber(int number) {
        return number + addend;
    }
}

条件.java

package my.example.conditions;

public interface Condition<T> {
    boolean appliesTo(T obj);
}

常量条件.java

package my.example.conditions;

public class ConstantCondition<T> implements Condition<T> {
    private boolean constant;

    public ConstantCondition(boolean alwaysReturnThisValue) {
        constant = alwaysReturnThisValue;
    }

    @Override
    public boolean appliesTo(T target) {
        return constant;
    }
}

NumberIsEvenCondition.java

package my.example.conditions;

public class NumberIsEvenCondition implements Condition<Integer> {
    @Override
    public boolean appliesTo(Integer i) {
        return (i % 2 == 0);
    }
}
于 2013-08-19T20:11:58.380 回答