【问题标题】:Java polymorphism: finding the right design patternJava 多态性:找到正确的设计模式
【发布时间】:2017-12-09 08:41:52
【问题描述】:

免责声明:我知道有很多关于多态性的问题,但我找不到适合我的问题的答案。如果你的 Google-fu 比我的好,请见谅。

我有一个使用继承的模型,如下例所示。

public abstract class Base {
// ...
}

public class ConcreteA extends Base {
    private String someString;
// ...
}

public class ConcreteB extends Base {
    private boolean someBool;
// ...
}

我还有一个List<Base>,它由ConcreteAs 或ConcreteBs 的对象组成。

我需要为列表中的每个对象生成一个图形视图,但生成的元素对于ConcreteAs 和ConcreteBs 是不一样的。在上面的示例中,ConcreteA 的视图将是一个文本字段,而 ConcreteB 的视图将是一个复选框。

如何使用 OO 原则实现这一目标?

【问题讨论】:

  • 不太清楚“ConcreteAConcreteB 的结果元素不同”是什么意思。但总的来说,OO 原则规定 Object 应该主要对它的所有操作负责,包括渲染。所以 OO 原则会规定一个renderTo(Media) 方法,但那是它自己的蠕虫罐头的全部负载。所以尝试使用类似专门的渲染器实例,每个都知道如何渲染什么。
  • 让我更新问题以使这部分更清晰。

标签: java oop design-patterns polymorphism


【解决方案1】:

您遇到的问题是,当调用者必须知道具体类型时,您在某处返回 List<Base>

这通常是因为人们试图使一种方法更通用。例如。如果有人有这个服务方法

public List<ConcreteA> doSomethingA(){ ... }
public List<ConcreteB> doSomethingB(){ ... }

他可能认为引入超类Base 是一个更好的主意,这样两种方法都可以替换为

public List<Base> doSomething(){ ... }

如果调用者只对Base 对象感兴趣,这是一个好主意。这意味着ConcreateAConcreteB 具有调用者仅依赖的一些共同行为。

但在您的情况下,调用者似乎需要不再可用的具体类型信息,因为更通用的方法。

因此,您必须保留或重构类型信息

  1. 通过使用自定义返回类型而不是使方法泛型来保留类型

     public class Result {
         private List<ConcreteA> concreteA;
         private List<ConcreteB> concreteA;
     }
    
     public Result doSomething();
    
  2. 使用instanceof重新构造类型信息

  3. 通过引入访问者模式重构类型信息。

【讨论】:

  • 我喜欢这个答案。我正要接受建议访问者模式的那个,因为这是我要解决的问题,而您提供的其他详细信息巩固了这一选择。从业务的角度来看,它确实只是一个List&lt;Base&gt;,但每个具体类的行为不同,我不想将所有内容都包含在业务模型中。感谢您花时间写这篇文章。 :)
【解决方案2】:

在这种情况下,我通常使用类似这样的泛型

public abstract  class Base <T extends  Shape>{
public abstract T drawShape();

}

public class ConcreatA extends Base<Circle> {
@Override
public Circle drawShape() {
    return null;
}

}

public class ConcreatB extends Base<Square> {
@Override
public Square drawShape() {
    return null;
}

}

所以现在你可以使用形状列表

【讨论】:

    【解决方案3】:

    我认为您正在寻找 Visitor Design Pattern

    来自维基百科:

    在面向对象的编程和软件工程中,访问者 设计模式是一种将算法与对象分离的方法 它运行的结构。这种分离的实际结果 是向现有对象结构添加新操作的能力 无需修改结构。这是遵循的一种方式 开闭原则。

    本质上,访问者允许将新的虚函数添加到 类的族,而不修改类。取而代之的是一个访客 创建的类实现了所有适当的 虚函数的特化。访客拿走 实例引用作为输入,通过double实现目标 调度。

    【讨论】:

    • 不确定这是Visitor - 对我来说更像Bridge
    • 我明白你的意思。但在这种情况下,我认为最好将结构和模型(ConcreteAConcreteBList&lt;Base&gt;)与行为(绘图)分开。桥接设计模式并非如此。
    • 我希望我能接受两个答案。访客模式看起来像是要走的路(感谢您提出建议),但 René 的回答提供了非常有用的信息。
    【解决方案4】:

    不是一种模式——这就是抽象的意义所在。声明一个您希望Base 的所有子类实现的方法,并且每个子类都必须以自己的方式实现它。

    显然你会传递参数和/或获取方法的结果。

    public abstract class Base {
        abstract void graphicalView();
    }
    
    public class ConcreteA extends Base {
        @Override
        void graphicalView() {
    
        }
    }
    
    public class ConcreteB extends Base {
        @Override
        void graphicalView() {
    
        }
    }
    
    public void test() throws IOException {
        List<Base> bases = new ArrayList<>();
        for ( Base b : bases ) {
            b.graphicalView();
        }
    }
    

    【讨论】:

    • 有没有办法在保持这种行为的同时将此功能从模型类中导出?我觉得处理图形界面与模型无关。
    • @RichouHunter 然后让graphicalView 返回一个可以渲染的对象。也许让它返回一些 XML,然后您可以通过 XSLT 转换为 HTML 运行。或者可能返回一个State,然后可以在外部呈现。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-02-18
    • 1970-01-01
    • 2019-11-05
    • 1970-01-01
    • 2013-11-23
    • 2015-12-23
    相关资源
    最近更新 更多