4

我有两个接口QueryFilterQuery在示例中是一个用于简化的类,我现在有 1 个查询),我现在想要Query.applyFilter()根据过滤器的真实情况编写函数,即不同的函数和NameFilter每个其他过滤器。DateFilter

我的解决方案如下:

interface Filter {
    public abstract void modifyQuery(Query query);
};

class NameFilter implements Filter{
    public void modifyQuery(Query query){
        query.applyFilter(this);
    }
};

class DateFilter implements Filter{
    public void modifyQuery(Query query){
        query.applyFilter(this);
    }
};

class Query {
    public void applyFilter(Filter filter){
        filter.modifyQuery(this);
    }

    void applyFilter(NameFilter* filter) {
        //"applying NameFilter";
    }

    void applyFilter(DateFilter* filter) {
       //apply dateFilter
   }
}

好的,这里我需要modifyQuery()为每个 Filter 类重写实现。

然后,我有如何在 C++ 中避免它的解决方案:我们使用模板并强制转换modifyQuery()

#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <vector>

using namespace std;

class Query;

class IFilter
{
public:
  virtual void modifyQuery(Query* query) = 0;
};

template <typename T>
class Filter : public IFilter
{
public:
  virtual void modifyQuery(Query* query);
};

class DateFilter;
class NameFilter;

class Query
{
public:

  void applyFilter(IFilter* filter)
  {
    cout << "applying Filter" << endl;
    filter->modifyQuery(this);
  }

  void applyFilter(NameFilter* filter)
  {
    cout << "applying NameFilter" << endl;
  }

  void applyFilter(DateFilter* filter)
  {
    cout << "applying DateFilter" << endl;
  }
};

template <typename T>
void Filter<T>::modifyQuery(Query* query)
{
  query->applyFilter(dynamic_cast<T*> (this));
}

class DateFilter : public Filter<DateFilter>
{
};

class NameFilter : public Filter<NameFilter>
{
};

int main()
{
  Query* query = new Query();
  IFilter* nameFilter = new NameFilter();
  IFilter* dateFilter = new DateFilter();

  std::vector<IFilter*> filterList;
  filterList.push_back(nameFilter);
  filterList.push_back(dateFilter);

  for (int i = 0; i < 2; ++i)
  {
    query->applyFilter(filterList[i]);
  }
  return 0;
}

演示

但是我不能在 Java 中使用这个解决方案,因为泛型与模板的操作不同。在 Java 中可以使用什么来避免复制粘贴?

4

2 回答 2

2

在寻找正确的方法时,Java 不会考虑方法参数的运行时类型。Java 只是简单地解释变量类型而不是值。

解决方案:

Java 6 注解可用于注解方法并实现多方法和值分派。所有这些都可以在运行时完成,不需要任何特殊的编译或预处理,并且使用仍然可以合理地方便用户使用。

我们需要引入两个“简单”的注解来进行注解:

Methods: 这个方法实现了什么多方法?

Parameters: 我们应该派送什么价值?

然后,我们可以处理注释并构建实现特定多方法的方法列表。此列表需要排序,以便最具体的方法排在第一位。“最具体”是指对于每个方法参数(从左到右),参数类型/值更加专业(例如,它是子类或与指定值匹配)。调用多方法意味着调用最具体的适用方法。“适用”意味着方法原型与实际运行时参数匹配,“最具体”意味着我们可以简单地搜索排序列表并找到第一个适用的。

注释处理可以封装在一个类中,然后可以在用户定义的方法中使用,该方法将简单地使用实际运行时参数调用多方法调度代码。

执行

Multi接口实现了一个运行时方法注解,用于标记多方法:

package jmultimethod;

import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Retention;
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Multi {

    public String value();
}

接口V实现了一个运行时参数注解,用于指定调度值:

package jmultimethod;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface V {

    public String value();
}

多方法代码如下:

package jmultimethod;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;

public class Multimethod {

    protected String name;
    protected final ArrayList<Method> methods = new ArrayList<Method>();
    protected final MethodComparator methodComparator = new MethodComparator();

    public Multimethod(String name, Class... classes) {
        this.name = name;
        for(Class c: classes) {
            add(c);
        }
    }

    public void add(Class c) {
        for(Method m: c.getMethods()) {
            for(Annotation ma: m.getAnnotations()) {
                if(ma instanceof Multi) {
                    Multi g = (Multi) ma;
                    if(this.name.equals(g.value())) {
                        methods.add(m);
                    }
                }
            }
        }
        sort();
    }

    protected void sort() {
        Method[] a = new Method[methods.size()];
        methods.toArray(a);
        Arrays.sort(a, methodComparator);
        methods.clear();
        for(Method m: a) {
            methods.add(m);
        }
    }

    protected class MethodComparator implements Comparator<Method> {
        @Override
        public int compare(Method l, Method r) {
            // most specific methods first 
            Class[] lc = l.getParameterTypes();
            Class[] rc = r.getParameterTypes();
            for(int i = 0; i < lc.length; i++) {
                String lv = value(l, i);
                String rv = value(r, i);
                if(lv == null) {
                    if(rv != null) {
                        return 1;
                    }
                }
                if(lc[i].isAssignableFrom(rc[i])) {
                    return 1;
                }
            }
            return -1;
        }
    }

    protected String value(Method method, int arg) {
        Annotation[] a = method.getParameterAnnotations()[arg];
        for(Annotation p: a) {
            if(p instanceof V) {
                V v = (V) p;
                return v.value();
            }
        }
        return null;
    }

    protected boolean isApplicable(Method method, Object... args) {
        Class[] c = method.getParameterTypes();
        for(int i = 0; i < c.length; i++) {
            // must be instanceof and equal to annotated value if present 
            if(c[i].isInstance(args[i])) {
                String v = value(method, i);
                if(v != null && !v.equals(args[i])) {
                    return false;
                }
            } else {
                if(args[i] != null || !Object.class.equals(c[i])) {
                    return false;
                }
            }
        }
        return true;
    }

    public Object invoke(Object self, Object... args) {
        Method m = null; // first applicable method (most specific)
        for(Method method: methods) {
            if(isApplicable(method, args)) {
                m = method;
                break;
            }
        }
        if(m == null) {
            throw new RuntimeException("No applicable method '" + name + "'.");
        }
        try {
            return m.invoke(self, args);
        } catch (Exception e) {
            throw new RuntimeException("Method invocation failed '" + name + "'.");
        }
    }
}

要使用多方法,用户代码必须:

  1. 使用多方法名称注释方法,例如

    @Multi("myMultimethod")
    
  2. 注释方法的名称可以是 Java 喜欢的任何名称。它很可能与多方法名称不同,因为某些方法可能具有足够相似的原型,从而导致 Java 编译器的名称冲突(并且可能是因为编译器可能会遇到空值问题)。此外,该方法应该对 Multimethod 类是可见的(例如,公共的)。

  3. 通过创建 Multimethod 对象来处理注释,例如

     protected Multimethod mm = new Multimethod("myMultimethod", getClass());
    
  4. 根据需要使用通用参数定义多方法“入口点”方法。该方法使用上面创建的 Multimethod 对象进行调度,例如

     public void myMultimethod(Object X, Object Y) {
       mm.invoke(this, X, Y);
    }
    
  5. 然后,可以像任何普通的 Java 方法一样调用多方法,例如

我的多方法(1,空);

限制:

值分派仅适用于 Java 注释支持的值,例如 String 类型的值。

以下代码基于 Multiple Dispatch 示例

package jmultimethod;

public class AsteroidTest {

    class Asteroid {}

    class Spaceship {}

    @Multi("collide")
    public void collideOO(Object X, Object Y) {
       log("?? Bang, what happened? ", X, Y);
    }

    @Multi("collide")
    public void collideAA(Asteroid X, Asteroid Y) {
        log("AA Look at the beautiful fireworks! ", X, Y);
    }

    @Multi("collide")
    public void collideAS(Asteroid X, Spaceship Y) {
        log("AS Is it fatal? ", X, Y);
    }

    @Multi("collide")
    public void collideSA(Spaceship X, Asteroid Y) {
        log("SA Is it fatal? ", X, Y);
    }

    @Multi("collide")
    public void collideSS(Spaceship X, Spaceship Y) {
        log("SS Who's fault was it? ", X, Y);
    }

    @Multi("collide")
    public void collide1S(String X, Spaceship Y) {
        log("1S any string? ", X, Y);
    }

    @Multi("collide")
    public void collide2S(@V("hi") String X, Spaceship Y) {
        log("2S 'hi' value? ", X, Y);
    }

    protected Multimethod mm = new Multimethod("collide", getClass());

    public void collide(Object X, Object Y) {
        mm.invoke(this, X, Y);
    }

    public void run() {
        Object A = new Asteroid();
        Object S = new Spaceship();
        collide(A, A);
        collide(A, S);
        collide(S, A);
        collide(S, S);
        collide(A, 1);
        collide(2, A);
        collide(S, 3);
        collide(4, S);
        collide(5, null);
        collide(null, null);
        collide("hi", S);
        collide("hello", S);
    }

    public void log(Object... args) {
        for(Object o: args) {
            if(o instanceof String) {
                System.out.print(" " + (String) o);
            } else {
                System.out.print(" " + o);
            }
        }
        System.out.println();
    }

    public static void main(String[] args) throws Exception {
        AsteroidTest t = new AsteroidTest();
        t.run();
    }
}

程序输出(部分编辑以适应屏幕)是:

AA Look at the beautiful fireworks!  Asteroid@1f24bbbf Asteroid@1f24bbbf
AS Is it fatal?  Asteroid@1f24bbbf Spaceship@24a20892
SA Is it fatal?  Spaceship@24a20892 Asteroid@1f24bbbf
SS Who's fault was it?  Spaceship@24a20892 Spaceship@24a20892
?? Bang, what happened?  Asteroid@1f24bbbf 1
?? Bang, what happened?  2 Asteroid@1f24bbbf
?? Bang, what happened?  Spaceship@24a20892 3
?? Bang, what happened?  4 Spaceship@24a20892
?? Bang, what happened?  5 null
?? Bang, what happened?  null null
2S 'hi' value?  hi Spaceship@24a20892
1S any string?  hello Spaceship@24a20892
于 2012-12-25T18:58:31.780 回答
1

提议:

interface Filter {
    public abstract void modifyQuery(Query query);
};

class NameFilter implements Filter{
    public void modifyQuery(Query query){
        // Modify query based on "Name"...
    }
};

class DateFilter implements Filter{
    public void modifyQuery(Query query){
        // Modify query based on "Date"...
    }
};

class Query {
    public void applyFilter(Filter filter){
        filter.modifyQuery(this);
    }

    // No need for other applyFilter() methods - all filters are instances of Filter.
}
于 2012-12-25T20:11:38.403 回答