使用抽象类作为参数的方法的不同实现的Java设计模式

使用抽象类作为参数的方法的不同实现的Java设计模式,java,design-patterns,polymorphism,Java,Design Patterns,Polymorphism,我的程序中有一个概念问题 我有4个类(ChildA、ChildB、ChildC),它们扩展了父类(parent) 我有一个这样的方法: public void doInteraction(Parent a, Parent b){ a.doActionWith(b); b.doActionWith(a); } 我的3个子类和父类: public abstract class Parent { public abstract void doActionWith(Parent other);

我的程序中有一个概念问题

我有4个类(ChildA、ChildB、ChildC),它们扩展了父类(parent)

我有一个这样的方法:

public void doInteraction(Parent a, Parent b){
a.doActionWith(b);
b.doActionWith(a);
}
我的3个子类和父类:

public abstract class Parent {
    public abstract void doActionWith(Parent other);
}

class ChildA extends Parent {

    @Override
    public void doActionWith(Parent other) {
        System.out.println("childA use doActionWith abstract");
    }

    public void doActionWith(ChildA chila) {
        System.out.println("childA use doActionWith childA");
    }

    public void doActionWith(ChildB chilb) {
        System.out.println("childA use doActionWith childB");
    }

    public void doActionWith(ChildC chilb) {
        System.out.println("childA use doActionWith childC");
    }

}

class ChildB extends Parent {

    @Override
    public void doActionWith(Parent other) {
        System.out.println("childB use doActionWith abstract");
    }

    public void doActionWith(ChildA childa) {
        System.out.println("childB use doActionWith childA");
    }

    public void doActionWith(ChildB chilb) {
        System.out.println("childB use doActionWith childB");
    }

    public void doActionWith(ChildC chilb) {
        System.out.println("childB use doActionWith childC");
    }

}

class ChildC extends Parent {

    @Override
    public void doActionWith(Parent other) {
        System.out.println("childB use doActionWith abstract");
    }

    public void doActionWith(ChildA childa) {
        System.out.println("childB use doActionWith childA");
    }

    public void doActionWith(ChildB chilb) {
        System.out.println("childB use doActionWith childB");
    }

    public void doActionWith(ChildC chilc) {
        System.out.println("childC use doActionWith childC");
    }

}
当我调用a.doActionWith(b);内部交互方法: 当jvm知道a和b的真实类型时,总是使用抽象方法

为什么java不使用参数完全匹配参数类型的特定方法

我找到的唯一解决方案是将对象强制转换为其子类型。 但我需要用的实例检查它们的类型。(想象一下,如果我有20个子类)。 我记得我的研究表明,一个怀孕的例子表明了一个坏的概念

我尝试了一些东西,比如

    public abstract <C extends Parent> void doActionWith(C other);
public-abstract-void-doActionWith(C-other);
但还是一样。 我想象一个设计模式可以解决这个问题,但我没有找到它。 也许我应该使用界面,但我不知道该怎么做

谢谢你的阅读


fabien.

您同时依赖静态绑定和动态绑定,但其中至少有一个不必要地损害了您的代码:

abstract Parent.doActionWith(Parent)
允许您实现两件事:

  • 当在
    ChildA
    ChildB
    ChildC
    中实现时,可以对行为进行自定义,使其完全符合这些子类中所需的内容。这就是动态绑定的好处。但是,动态绑定基于调用方法的对象,而不是传递的参数的运行时类。这意味着在您的情况下,编译器将始终选择
    Parent.doActionWith(Parent)
    ,因为这是
    Parent
    类型所知道的唯一一个
  • 您可以使用键入为
    Parent
    的任何子类的参数调用
    doActionWith
    方法。但是,由于调用方法的对象的静态(编译时)类型是
    Parent
    ,因此选择的方法将始终是
    doActionWith(Parent)
    ;同样,因为
    Parent
    只知道
    doActionWith(Parent)
简要回顾一下:

  • 编译器决定调用方法的哪个签名。这是基于对象的声明数据类型。
    因为声明的参数类型是
    Parent
    ,那么参数的运行时类是什么并不重要:调用的方法签名将声明
    Parent
    为参数类型的方法签名
  • 运行时决定将执行编译时选择的签名的哪个实现。您得到了这个结果,但它一定是子类(
    ChildA
    ChildB
    ChildC
    )中的参数类型为
    Parent
    的方法,具体取决于调用该方法的对象的运行时类)
换句话说,如果希望同时应用静态和动态绑定,则需要:

  • 声明抽象类中的所有重载方法
  • doInteraction(父级a、父级b)
    的签名更改为在参数列表中包含所有适用子类的组合
  • 显然,这是不可取的。以下是我的观点:我会简化事情:

  • 摆脱重载方法
  • 在每个子类中重写
    doActionWith(Parent)
    的相同签名的实现

  • 有了它,关键是如何用方法覆盖
    doActionWith。理想情况下,此方法不需要知道其参数的运行时类,这是一件好事。

    在一句话中,您的问题是您想要使用它,而Java不直接支持它。换句话说,您希望根据两个参数的运行时类型选择方法实现

    因为Java不支持它,所以必须模拟该机制。有不同的方法可以做到这一点。这是一个,但如果你真的有一个具体的子类可以与任何其他具体的子类一起工作的情况,这并不理想

    这是另一个基于服务提供者接口概念的解决方案。基本上,子类注册它们的方法实现,并且基于目标参数的分派由继承的方法显式完成。这是一个简化的代码示例,假设最终的子类,但可以扩展它以支持额外的多态性灵活性

    public abstract class Parent {
    
        protected IdentityHashMap<Class<?>, Consumer<Parent>> handlers 
            = new IdentityHashMap<>();
    
        public final void doActionWith(Parent p) {
            if( !handlers.containsKey(p.getClass())) 
                throw new UnsupportedOperationException();
    
            handlers.get(p.getClass()).accept(p);
        }
    }
    
    final class A extends Parent {
        public A() {
            handlers.put(A.class, a -> System.out.println("A works with A"));
            handlers.put(B.class, b -> System.out.println("A works with B"));
            handlers.put(C.class, c -> System.out.println("A works with C"));
        }
    }
    
    final class B extends Parent {
        public B() {
            handlers.put(A.class, a -> System.out.println("B works with A"));
            handlers.put(B.class, b -> System.out.println("B works with B"));
            handlers.put(C.class, c -> System.out.println("B works with C"));
        }
    }
    
    final class C extends Parent {
        public C() {
            handlers.put(A.class, a -> System.out.println("C works with A"));
            handlers.put(B.class, b -> System.out.println("C works with B"));
            handlers.put(C.class, c -> System.out.println("c works with C"));
        }
    }
    
    您可以使用:


    访问者模式是解决这个问题的一种方法。在我看来,这很难看,也很难调试,但有时它仍然是必要的。如果在最重要的时候使用
    父级
    抽象对您没有帮助,那么使用它又有什么意义呢?(类
    A
    B
    C
    之间的关系在您需要使用它们的公共
    父类时对您没有帮助)如果我尝试使
    C
    C
    的子类交互,这将失败。另外,我建议添加
    受保护的void addHandler(Class clazz,ConsumerThanks)。我试图将其缩减到最低限度,但更多的错误检查不会有什么坏处。
    
    protected <T extends Parent> void addHandler(Class<T> clazz, 
        Consumer<? super T> handler) {
        handlers.put(clazz, (Consumer<Parent>)handler);
    }
    
    public interface Parent {
        default void actWith(Parent parent) {
            parent.accept(this);
        }
        void accept(Parent parent);
        void doActionWith(ChildA childA);
        void doActionWith(ChildB childB);
        void doActionWith(ChildC childC);
    }
    
    public class ChildA implements Parent {
        @Override
        public void accept(Parent parent) {
            parent.doActionWith(this);
        }
    
        @Override
        public void doActionWith(ChildA childA) {
            System.out.println("childA use doActionWith childA");
        }
    
        @Override
        public void doActionWith(ChildB childB) {
            System.out.println("childA use doActionWith childB");
        }
    
        @Override
        public void doActionWith(ChildC childC) {
            System.out.println("childA use doActionWith childC");
        }
    }
    
    public class ChildB implements Parent {
        @Override
        public void accept(Parent parent) {
            parent.doActionWith(this);
        }
    
        @Override
        public void doActionWith(ChildA childA) {
            System.out.println("childB use doActionWith childA");
        }
    
        @Override
        public void doActionWith(ChildB childB) {
            System.out.println("childB use doActionWith childB");
        }
    
        @Override
        public void doActionWith(ChildC childC) {
            System.out.println("childB use doActionWith childC");
        }
    }
    
    public class ChildC implements Parent {
        @Override
        public void accept(Parent parent) {
            parent.doActionWith(this);
        }
    
        @Override
        public void doActionWith(ChildA childA) {
            System.out.println("childC use doActionWith childA");
        }
    
        @Override
        public void doActionWith(ChildB childB) {
            System.out.println("childC use doActionWith childB");
        }
    
        @Override
        public void doActionWith(ChildC childC) {
            System.out.println("childC use doActionWith childC");
        }
    }
    
    public static void interact(Parent a, Parent b) {
        a.actWith(b);
        b.actWith(a);
    }