通过java中的参数集查找最合适的对象

时间:2014-01-22 08:35:00

标签: java search data-structures

我有一组对象。此对象根据请求参数计算某些数字。我们称他们为计算器。每个计算器都有描述这个计算器最适合的指定类型的请求。 例如,

Calculator1 : with this parameters : price > 10, gender = male, geo_id = 1, 2 or 3.
Calculator2 : with this parameters : price < 5, gender = male,  geo_id = 1, 2. 

对于请求:price = 11, gender = male, geo_id = 2我应该得到最适合的calculator1,然后是calculator2。

对于请求:price = 4, gender = male, geo_id = 2我应该得到calculator2然后是calculator1。

对于请求:price = 3, gender = female, geo_id = 5我应该只获得第二个。

现在我正在和Lucene一起做,但它并不适合这项任务。你能推荐我一些图书馆或方法吗?

4 个答案:

答案 0 :(得分:2)

我的建议是使用比较器。请参阅以下课程的草图

import java.util.HashMap;
import java.util.Map;

public abstract class Calculator {
    public static Map<String, Integer> weights;
    static {
        weights = new HashMap<String, Integer>();
        weights.put("price", 10);
        weights.put("gender", 2);
        weights.put("geo", 5);
    }

    public abstract int calculate(Map<String, Integer> request);
    public abstract int fitnessFor(Map<String, Integer> request);
}

您可以使用权重来调整各个请求参数的相对重要性。

import java.util.Map;

public class Calculator1 extends Calculator {

    public int calculate(Map<String, Integer> request) {
        return -1;
    }

    @Override
    public int fitnessFor(Map<String, Integer> request) {
        int fitness = -1;
        Integer price = request.get("price");
        if (price == null)
            return fitness;

        if (price > 10)
            fitness += weights.get("price");

        return fitness;
    }

    public String toString() { return "Calculator1"; }
}

Calculator1只关心昂贵的物品。

import java.util.Map;

public class Calculator2 extends Calculator {

    public int calculate(Map<String, Integer> request) {
        return -1;
    }

    @Override
    public int fitnessFor(Map<String, Integer> request) {
        int fitness = -1;
        Integer price = request.get("price");
        if (price == null)
            return fitness;

        if (price < 5)
            fitness += weights.get("price");

        Integer gender = request.get("gender");
        if (gender == null)
            return fitness;

        if (gender == 1)
            fitness += weights.get("gender");

        return fitness;
    }

    public String toString() { return "Calculator2"; }  
}

Calculator2关注价格较低的商品esp。如果他们是性别1。

比较器只比较计算器相对于请求的适应度:

import java.util.Comparator;
import java.util.Map;

public class CalcComparator implements Comparator<Calculator> {
    private Map<String, Integer> request;

    public CalcComparator(Map<String, Integer> request) {
        this.request = request;
    }

    @Override
    public int compare(Calculator c1, Calculator c2) {
        int c1Fitness = c1.fitnessFor(request);
        int c2Fitness = c2.fitnessFor(request);

        if (c1Fitness == c2Fitness)
            return 0;

        if (c1Fitness < c2Fitness)
            return 1;

        return -1;
    }
}

尝试使用:

public class Main {

    public static void main(String[] args) {
        Map<String, Integer> request = new HashMap<String, Integer>();
        request.put("price", 5);
        request.put("gender", 1);

        List<Calculator> calculators = new ArrayList<Calculator>();
        calculators.add(new Calculator1());
        calculators.add(new Calculator2());

        Collections.sort(calculators, new CalcComparator(request));

        System.out.println("For request: "+request);
        for (Calculator c : calculators) {
            System.out.println("\t"+c.toString() + "( fitness " + c.fitnessFor(request) + ")");
        }
    }
}

这只是一个草图来说明这个想法。您可能希望为请求参数引入一个枚举,可能会引入一个Request类,很可能完全改变计算适应性的方法,将某些字段设为私有并封装它们等等。

优势在于您可以根据请求的适用性轻松获得所有计算器的订购。

答案 1 :(得分:1)

您可以尝试这样的事情:

public enum Calculator
{
    CALC1
    {
        @Override
        protected int matchCount( Map parameters )
        {
            // TODO count how many conditions match
            return 0;
        }

        @Override
        protected int calc( Map parameters )
        {
            // TODO
            return 0;
        }
    },

    CALC2
    {
        @Override
        protected int matchCount( Map parameters )
        {
            // TODO count how many conditions match
            return 0;
        }

        @Override
        protected int calc( Map parameters )
        {
            // TODO
            return 0;
        }
    };

    protected abstract int matchCount( Map parameters );
    protected abstract int calc( Map parameters );

    public int doCalc( Map parameters )
    {
        Calculator  mostSuited = null;
        int  maxCount = 0;

        for ( Calculator  calc : values() )
        {
            int  matchCount = calc.matchCount( parameters );

            if ( matchCount > maxCount )
            {
                mostSuited = calc;
            }
        }

        return mostSuited.calc( parameters );
    }
}

您使用上述方法的方法是调用:int result = Calculator.doCalc( parameters )

答案 2 :(得分:1)

如果我理解正确,我建议您使用Specification设计模式,这种模式在这种情况下使用。像Lucene这样的高档图书馆没有必要做这么简单的任务。规范模式的优点是它可以对所有过滤逻辑进行分组和封装。您的实现可能会有所不同,但下面是一个简单的示例

public interface Specification<T> {
    boolean isSatisfiedBy(T candidate);
    Specification<T> and(Specification<T> specification);
    Specification<T> or(Specification<T> specification);
    Specification<T> not(Specification<T> specification);
}

public abstract class Calculator {
    // ...
}

public class Calculator1 extends Calculator implements Specification<Request> {
    public boolean isSatisfiedBy(Request request) {
        // check if the request fits this calculator
    }
}

public class Calculator2 extends Calculator implements Specification<Request> {
    public boolean isSatisfiedBy(Request request) {
        // check if the request fits this calculator
    }
}

然后,您可以拥有一个或一组计算器

public class Calculators {
    private final List<RequestSpecification> calculators;
    public Calculator getOneSuitedFor(Request request) {
        for (Calculator calculator : calculators) {
            if (calculator.isSatisfiedBy(request)) {
                return calculator;
            }
        }
        return null;
    }
}

在这里你将如何使用它

Calculator calculator = Calculators.getOneSuitedFor(request);

或者,如果需要,您可以通过使用合成(参见上面的参考链接)继续进行扩展,这允许逻辑链接和根据上下文组合不同的规范。然而,这需要与上面的类设计略有不同,但更灵活

final Request request;

Specification<Calculator> price = new Specification<>() {
     public boolean isSatisfiedBy(Calculator calculator) {
         return calculator.supportsPrice(request.getPrice());
     }
};

Specification<Calculator> gender = new Specification<>() {
     public boolean isSatisfiedBy(Calculator calculator) {
         return calculator.supportsGender(request.getGender());
     }
};

Specification<Calculator> region = new Specification<>() {
     public boolean isSatisfiedBy(Calculator calculator) {
         return calculator.supportsRegion(request.getRegion());
     }
};

Specification calcSpec = price.and(gender).and(region);
boolean isSatisfied = calcSpec.isSatisfiedBy(calculator);

另一个有趣的例子是使用命名规范

Specification<Calculator> teenager = new Specification<>() {
     public boolean isSatisfiedBy(Calculator calculator) {
         return calculator.getAge() >= 13 && calculator.getAge() <= 19;
     }
};

Specification<Calculator> male = new Specification<>() {
     public boolean isSatisfiedBy(Calculator calculator) {
         return calculator.getGender().equals("male");
     }
};

Specification<Calculator> fromEurope = new Specification<>() {
     public boolean isSatisfiedBy(Calculator calculator) {
         return calculator.getRegion().equals("Europe");
     }
};

Specification<Calculator> calcSpec = teenager.and(male).and(fromEurope);
boolean isSatisfied = calcSpec.isSatisfiedBy(calculator);

答案 3 :(得分:0)

创建一个计算器基类:

public static abstract class Calculator {

    // This Contains the common score calculation methods.
    public int getScore(int price, String gender, int geo_id) {
        int score = 0;
        if (gender.equalsIgnoreCase("male"))
            score++;
        if (getGeoIds().contains(geo_id))
            score++;
        return score;
    }

    public ArrayList<Integer> getGeoIds() {
        // Fetching the common list of geo points to be compared.
        ArrayList<Integer> lst = new ArrayList<Integer>();
        lst.add(1);
        lst.add(2);
        return lst;
    }

    public abstract void doCalculation();

}

然后通过从此基础扩展来创建计算器类。

public static class Calcualtor1 extends Calculator {

    @Override
    public int getScore(int price, String gender, int geo_id) {
        // fetching score from common score calculation.
        int score = super.getScore(price, gender, geo_id);
        // Adding its own score logic.
        if (price > 10)
            score++;
        return score;
    }

    @Override
    public void doCalculation() {
        // Do your actual work.
    }

    @Override
    public ArrayList<Integer> getGeoIds() {
        ArrayList<Integer> lst = super.getGeoIds();
        // Adding the geo id to compare for this calculator.
        lst.add(3);
        return lst;
    }

}

public static class Calcualtor2 extends Calculator {

    @Override
    public int getScore(int price, String gender, int geo_id) {
        // fetching score from common score calculation.
        int score = super.getScore(price, gender, geo_id);
        // Adding its own score logic.
        if (price < 5)
            score++;
        return score;
    }

    @Override
    public void doCalculation() {
        // Do your actual work.
    }
}

初始值:

//To store the list of available calculators.
private static ArrayList<Class<? extends Calculator>> calculators;

static {
    //Initializing the calculator list in static constructor.
    calculators = new ArrayList<Class<? extends Calculator>>();
    calculators.add(Calcualtor1.class);
    calculators.add(Calcualtor2.class);
}

实际处理:

public static void main(String[] args) {
    int price = 10;
    String gender = "male";
    int geo_id = 2;

    Calculator calculator = null;
    int score = 0;

    for (Class<? extends Calculator> calClass : calculators) {

        Calculator cal = null;
        try {
            cal = calClass.newInstance();
        } catch (Exception e) {
            continue;
        }

        int calScore = cal.getScore(price, gender, geo_id);

        if (calScore > score) {
            calculator = cal;
            score = calScore;
        }
    }

    if (calculator != null) {
        calculator.doCalculation();
    }

}