假设我要在一个表中显示一个库存项目列表(使用Java)。域模型由抽象基类StockItem组成,各种其他类型的库存项目都从该基础类派生。 StockItem提供了一个最小接口(interface)(getId()和getDescription()),但除此之外,子类可能会有很大的不同。

如果我将自己限制在StockItem上定义的方法上,将无法向用户提供足够的详细信息,因此这意味着某些列将引用不适用于某些行的字段(例如,实物是可计数的,此计数应出现在表格中,而预期也会出现在表格中的服务项目则不可计数,在这种情况下,应显示“N/A”或类似内容。

以“Countable”为例,在我看来,有两种解决方案(但是请记住,Countable并不是唯一涉及的接口(interface))。

  • 使countable接口(interface)成为基类的一部分,并强制所有内容均变为Countable(即使不是)。没有意义的类将需要返回一些特殊值或引发异常,或者表明它们违反了StockItem的契约(Contract)。
  • 在我的迭代器中,使用大量instanceof检查并进行适当的转换。如果引入StockItem的新子类或以其他方式更改继承树,则必须记住要更改此代码。

  • 这两种方法对我来说似乎都是反模式,我很想听听可能会采用的其他更高级的方法。我怀疑这没有万能的法宝,但是如果其他语言具有使此类事情变得更轻松的功能,我也很想听到它们(不过出于普遍的兴趣,我不会在任何时候重新实现该系统不久:)

    谢谢,
    菲尔

    最佳答案

    适配器图案

    在您的StockItem类中添加方法:

    /**
     * Adapt the current instance to the type
     * denoted by clazz else return null
     * ...
     */
    public <T> T adapt(Class<T> clazz){
        if( clazz.isInstance(this)){
            return (T)this;
        }
        return null;
    }
    

    这将被所有子类继承,并允许调用者安全地转换类型。
    不要被泛型推迟了,只是说我希望此方法返回一个与clazz参数类型相同的实例。

    现在,您的表提供程序可以实现以下内容:
    public String getColumnText(Object cell, int columnIndex) {
        if (cell instanceof StockItem) {
            StockItem item = (StockItem) cell;
    
            switch(columnIndex) {
                case ID:
                    return item.getID;
                case DESCRIPTION:
                    return item.getDescription;
                case COUNT:
                Countable countableItem = item.adapt(Countable.class);
                    return countableItem == null ? "N/A" : countableItem.getCount();
            }
        }
        return "N/A";
    }
    

    08-19 17:21