Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/oop/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 多参数的多分派_Java_Oop_Dynamic_Parameter Passing_Dispatch - Fatal编程技术网

Java 多参数的多分派

Java 多参数的多分派,java,oop,dynamic,parameter-passing,dispatch,Java,Oop,Dynamic,Parameter Passing,Dispatch,在OO语言中,有没有一种优雅的方法可以通过(单个)动态分派为具有2个参数(甚至更多)的方法获得多个分派 可能出现的问题示例: 这是一个受Java启发的示例。(问题与语言无关!) 我有许多运算符和操作数具体类型,但仅通过其抽象类型引用它们: Operand a = new Integer() Operand b = new Matrix(); Operand result; Operator mul = new Multiplication(); result = mul.eval(a, b);

在OO语言中,有没有一种优雅的方法可以通过(单个)动态分派为具有2个参数(甚至更多)的方法获得多个分派

可能出现的问题示例:

这是一个受Java启发的示例。(问题与语言无关!)

我有许多运算符和操作数具体类型,但仅通过其抽象类型引用它们:

Operand a = new Integer()
Operand b = new Matrix();
Operand result;
Operator mul = new Multiplication();
result = mul.eval(a, b); // Here I want Multiplication::eval(Integer, Matrix) to be called

我不确定我是否会回答你的问题,但我希望我能为讨论增添一点内容。稍后我会尝试给出一个更一般的答案,但在这篇文章中,我将只关注上面的例子

问题中给出的示例的问题在于,它基于算术运算,这使得它本身就很复杂,因为给定运算符的实现取决于其操作数的类型

我认为这个问题稍微掩盖了这个问题,例如,我们可以花时间尝试让您的示例发挥作用,而不是处理多个调度问题

让代码工作的一种方法是从不同的角度思考。我们可以做的不是定义一个称为
操作符的抽象,而是认识到操作数的固有性质,即它们必须提供可能影响它们的每一个可能操作的实现。用面向对象的术语来说,每个操作数都包含一系列可能影响它们的操作

因此,假设我有一个这样的接口
操作数
,它定义了操作数支持的所有可能的操作。请注意,我不仅为每个可能的已知操作数定义了一个方法方差,还为另一个未知操作数的一般情况定义了一个方法方差:

interface Operand {
    Operand neg();
    Operand add(Int that);
    Operand add(Decimal that);
    Operand add(Operand that);
    Operand mult(Int that);
    Operand mult(Decimal that);
    Operand mult(Operand that);
    Operand sub(Int that);
    Operand sub(Decimal that);
    Operand sub(Operand that);
}

现在请考虑我们有两个实现:<代码> int <代码>和<代码>十进制< /代码>(我将使用十进制而不是您的示例的矩阵来简化)。 然后我可以这样做:

Operand a = new Int(10);
Operand b = new Int(10);
Operand c = new Decimal(10.0);
Operand d  = new Int(20);

Operand x = a.mult(b).mult(c).mult(d);
Operand y = b.mult(a);

System.out.println(x); //yields 20000.0
System.out.println(y); //yields 100

Operand m = new Int(1);
Operand n = new Int(9);

Operand q = m.sub(n); 
Operand t = n.sub(m); 

System.out.println(q); //yields -8
System.out.println(t); //yeilds 8
这里的要点是:

  • 每个操作数实现的工作方式与访问者模式类似,即每个操作数实现都包含一个分派函数,用于可以获得的每个可能的组合
  • 棘手的部分是作用于任何
    操作数的方法。这是我们利用访客调度能力的地方,因为我们知道
    这个
    的确切类型,但不知道
    那个
    的确切类型,所以我们通过
    那个方法强制调度。方法(这个)
    解决了问题
  • 然而,由于我们颠倒了求值顺序,这就产生了一个不可交换的算术运算问题,比如减法运算。这就是为什么我用加法来做减法(即1-9等于1+-9)。因为加法是可交换的
就在这里。现在,我找到了一个具体示例的解决方案,但是我没有提供一个很好的解决方案来解决您最初遇到的多重分派问题。这就是为什么我说这个例子不够好

也许你可以提供一个更好的例子,比如维基百科页面上的例子

然而,我认为解决方案可能永远是,将问题简化为一系列单一的调度,用某种访问者模式解决,就像我做的那样。如果我有时间,我将尝试稍后给出一个更一般的答案,而不仅仅是这个具体的例子

但希望这篇文章有助于促进进一步的讨论,幸运的是,它朝着实际答案的方向迈出了一步。

鲍勃叔叔这样做了:

 // visitor with triple dispatch. from a post to comp.object by robert martin http://www.oma.com
    /*
    In this case, we are actually using a triple dispatch, because we have two
    types to resolve.  The first dispatch is the virtual Collides function which
    resolves the type of the object upon which Collides is called.  The second
    dispatch is the virtual Accept function which resolves the type of the
    object passed into Collides.  Now that we know the type of both objects, we
    can call the appropriate global function to calculate the collision.  This
    is done by the third and final dispatch to the Visit function.
    */
    interface AbstractShape
        {
        boolean Collides(final AbstractShape shape);
        void Accept(ShapeVisitor visitor);
        }
    interface ShapeVisitor
        {
        abstract public void Visit(Rectangle rectangle);
        abstract public void Visit(Triangle triangle);
        }
    class Rectangle implements AbstractShape
        {
        public boolean Collides(final AbstractShape shape)
            {
            RectangleVisitor visitor=new RectangleVisitor(this);
            shape.Accept(visitor);
            return visitor.result();
            }
        public void Accept(ShapeVisitor visitor)
            { visitor.Visit(this); } // visit Rectangle
        }
    class Triangle implements AbstractShape
        {
        public boolean Collides(final AbstractShape shape)
            {
            TriangleVisitor visitor=new TriangleVisitor(this);
            shape.Accept(visitor);
            return visitor.result();
            }
        public void Accept(ShapeVisitor visitor)
            { visitor.Visit(this); } // visit Triangle
        }

    class collision
        { // first dispatch
        static boolean Collides(final Triangle t,final Triangle t2) { return true; }
        static boolean Collides(final Rectangle r,final Triangle t) { return true; }
        static boolean Collides(final Rectangle r,final Rectangle r2) { return true; }
        }
    // visitors.
    class TriangleVisitor implements ShapeVisitor
        {
        TriangleVisitor(final Triangle triangle)
            { this.triangle=triangle; }
        public void Visit(Rectangle rectangle)
            { result=collision.Collides(rectangle,triangle); }
        public void Visit(Triangle triangle)
            { result=collision.Collides(triangle,this.triangle); }
        boolean result() {return result; }
        private boolean result=false;
        private final Triangle triangle;
        }
    class RectangleVisitor implements ShapeVisitor
        {
        RectangleVisitor(final Rectangle rectangle)
            { this.rectangle=rectangle; }
        public void Visit(Rectangle rectangle)
            { result=collision.Collides(rectangle,this.rectangle); }
        public void Visit(Triangle triangle)
            { result=collision.Collides(rectangle,triangle); }
        boolean result() {return result; }
        private boolean result=false;
        private final Rectangle rectangle;
        }
    public class MartinsVisitor
        {
        public static void main (String[] args)
            {
            Rectangle rectangle=new Rectangle();
            ShapeVisitor visitor=new RectangleVisitor(rectangle);
            AbstractShape shape=new Triangle();
            shape.Accept(visitor);
            }
        }

我决定补充这一点,因为上述两个答案有些不完整。我自己对这个问题很好奇,但找不到答案,所以不得不自己分析。一般来说,有两种方法可以用来实现多种语言,如C++或java语言,它们支持单一动态调度和类似类型或运行时类型识别。p> 对于双重分派情况,访问者模式是最常见的(对于Arg1->foo(Arg2)),在大多数情况下,我收集的信息比使用RTTI和switches或if语句要好。我们还可以通过将一系列调用树状结构中的方法的单个调度链接起来,从而将访问者方法推广到n的情况,即Arg1->foo(Arg2,Arg3..ArgN),该树状结构中的方法在参数类型上区分为一些k,并拆分k+1参数的方式数。例如,对于三重分派的简单情况,每种类型只有两个实例:

interface T1 {
    public void f(T2 arg2, T3 arg3);
}

interface T2 {
    public void gA(A a, T3 arg3)
    public void gB(B b, T3 arg3)
}

interface T3 {
    public void hAC(A a,C c);
    public void hAD(A a,D d);
    public void hBC(B b,C c);
    public void hBD(B b,D d);
}

class A implements T1 {
    public void f(T2 arg2, T3 arg3) {
        arg2->gA(this,arg3);
    }
}

class B implements T1 {
    public void f(T2 arg2, T3 arg3) {
        arg2->gB(this,arg3);
    }
}

class C implements T2 {
    public void gA(A a,T arg3) {
        arg3->hAC(a, this);
    }

    public void gB(B b,T arg3) {
        arg3->hBC(b, this);
    }
}

class D implements T2 {
    public void gA(A a,T arg3) {
        arg3->hAD(a, this);
    }

    public void gB(B b,T arg3) {
        arg3->hBD(b, this);
    }
}

class E implements T3 {
    public void  hAC(A a,C c) {
        System.out.println("ACE");
    }
    public void  hAD(A a,D d) {
        System.out.println("ADE");
    }
    public void  hBC(B b,C c) {
        System.out.println("BCE");
    }
    public void  hBD(B b,D d) {
        System.out.println("BDE");
    }

}

class F implements T3 {
    public void  hAC(A a,C c) {
        System.out.println("ACF");
    }
    public void  hAD(A a,D d) {
        System.out.println("ADF");
    }
    public void  hBC(B b,C c) {
        System.out.println("BCF");
    }
    public void  hBD(B b,D d) {
        System.out.println("BDF");
    }

}

public class Test {
    public static void main(String[] args) {
        A a = new A();
        C c = new C();
        E e = new E();

        a.f(c,e);
    }
}
虽然这种方法概括了问题,但问题是相当明显的。对于最坏情况下的每个端点函数,必须编写n-1个分派函数

可以通过instanceOf操作符实现与运行时类型标识类似的功能:

class Functions
{
    static void f(A a,C c,E e) {
        System.out.println("ACE");
    }
    static void f(A a,C c,F f) {
        System.out.println("ACF");
    }
    static void f(A a,D d,E e) {
        System.out.println("ADE");
    }
    static void f(A a,D d,F f) {
        System.out.println("ADF");
    }
    static void f(B b,C c,E e) {
        System.out.println("BCE");
    }
    static void f(B b,C c,F f) {
        System.out.println("BCF");
    }
    static void f(B b,D d,E e) {
        System.out.println("BDE");
    }
    static void F(B b,D d,F f) {
        System.out.println("BDF");
    }

    static void dispatch(T1 t1, T2 t2, T3 t3) {
        if( t1 instanceOf A)
        {
            if(t2 instance of C) {
                if(t3 instance of E) {
                    Function.F( (A)t1, (C)t2, (E)t3 );
                }
                else if(t3 instanceOf F) {
                    Function.F( (A)t1, (C)t2, (F)t3 );
                }
            }
            else if(t2 instance of D) {
                if(t3 instance of E) {
                    Function.F( (A)t1, (D)t2, (E)t3 );
                }
                else if(t3 instanceOf F) {
                    Function.F( (A)t1, (D)t2, (F)t3 );
                }
            }
        }
        else if(t1 instanceOf B) {
            if(t2 instance of C) {
                if(t3 instance of E) {
                    Function.F( (B)t1, (C)t2, (E)t3 );
                }
                else if(t3 instanceOf F) {
                    Function.F( (B)t1, (C)t2, (F)t3 );
                }
            }
            else if(t2 instance of D) {
                if(t3 instance of E) {
                    Function.F( (B)t1, (D)t2, (E)t3 );
                }
                else if(t3 instanceOf F) {
                    Function.F( (B)t1, (D)t2, (F)t3 );
                }
            }
        }
    }
}

第二个解决方案可以使用反射进一步简化。

您的示例evem编译吗?(数字到操作数的转换)。你能再澄清一点问题是什么吗?现在还不清楚你在寻找什么,我没有编写构造函数,所以它没有编译,这只是一个例子。当被调用的对象具有静态类型运算符和运行时类型乘法且其参数具有静态类型操作数和运行时类型Integer and Matrix时,我想要调用乘法::eval(Integer,Matrix)。存在这样一种模式,当eval方法只有一个参数时,称为多重分派。我希望对2个(或更多)参数使用相同的方法您仅限于
操作员定义的合同。因此,除非在
操作符
类(或其父类之一)中定义
eval(Integer,Matrix)
方法,否则将无法调用它。您可以将其更改为
乘法
,而不是将其声明为
运算符类型。
interface T1 {
    public void f(T2 arg2, T3 arg3);
}

interface T2 {
    public void gA(A a, T3 arg3)
    public void gB(B b, T3 arg3)
}

interface T3 {
    public void hAC(A a,C c);
    public void hAD(A a,D d);
    public void hBC(B b,C c);
    public void hBD(B b,D d);
}

class A implements T1 {
    public void f(T2 arg2, T3 arg3) {
        arg2->gA(this,arg3);
    }
}

class B implements T1 {
    public void f(T2 arg2, T3 arg3) {
        arg2->gB(this,arg3);
    }
}

class C implements T2 {
    public void gA(A a,T arg3) {
        arg3->hAC(a, this);
    }

    public void gB(B b,T arg3) {
        arg3->hBC(b, this);
    }
}

class D implements T2 {
    public void gA(A a,T arg3) {
        arg3->hAD(a, this);
    }

    public void gB(B b,T arg3) {
        arg3->hBD(b, this);
    }
}

class E implements T3 {
    public void  hAC(A a,C c) {
        System.out.println("ACE");
    }
    public void  hAD(A a,D d) {
        System.out.println("ADE");
    }
    public void  hBC(B b,C c) {
        System.out.println("BCE");
    }
    public void  hBD(B b,D d) {
        System.out.println("BDE");
    }

}

class F implements T3 {
    public void  hAC(A a,C c) {
        System.out.println("ACF");
    }
    public void  hAD(A a,D d) {
        System.out.println("ADF");
    }
    public void  hBC(B b,C c) {
        System.out.println("BCF");
    }
    public void  hBD(B b,D d) {
        System.out.println("BDF");
    }

}

public class Test {
    public static void main(String[] args) {
        A a = new A();
        C c = new C();
        E e = new E();

        a.f(c,e);
    }
}
class Functions
{
    static void f(A a,C c,E e) {
        System.out.println("ACE");
    }
    static void f(A a,C c,F f) {
        System.out.println("ACF");
    }
    static void f(A a,D d,E e) {
        System.out.println("ADE");
    }
    static void f(A a,D d,F f) {
        System.out.println("ADF");
    }
    static void f(B b,C c,E e) {
        System.out.println("BCE");
    }
    static void f(B b,C c,F f) {
        System.out.println("BCF");
    }
    static void f(B b,D d,E e) {
        System.out.println("BDE");
    }
    static void F(B b,D d,F f) {
        System.out.println("BDF");
    }

    static void dispatch(T1 t1, T2 t2, T3 t3) {
        if( t1 instanceOf A)
        {
            if(t2 instance of C) {
                if(t3 instance of E) {
                    Function.F( (A)t1, (C)t2, (E)t3 );
                }
                else if(t3 instanceOf F) {
                    Function.F( (A)t1, (C)t2, (F)t3 );
                }
            }
            else if(t2 instance of D) {
                if(t3 instance of E) {
                    Function.F( (A)t1, (D)t2, (E)t3 );
                }
                else if(t3 instanceOf F) {
                    Function.F( (A)t1, (D)t2, (F)t3 );
                }
            }
        }
        else if(t1 instanceOf B) {
            if(t2 instance of C) {
                if(t3 instance of E) {
                    Function.F( (B)t1, (C)t2, (E)t3 );
                }
                else if(t3 instanceOf F) {
                    Function.F( (B)t1, (C)t2, (F)t3 );
                }
            }
            else if(t2 instance of D) {
                if(t3 instance of E) {
                    Function.F( (B)t1, (D)t2, (E)t3 );
                }
                else if(t3 instanceOf F) {
                    Function.F( (B)t1, (D)t2, (F)t3 );
                }
            }
        }
    }
}