【问题标题】:Inheritance vs Composition?继承与组合?
【发布时间】:2011-05-20 10:41:52
【问题描述】:

一个月前我问过类似的问题:Inheritance though composition?

我从这篇文章中拿了一些例子:http://www.javaworld.com/javaworld/jw-11-1998/jw-11-techniques.html?page=1 然而,这一次是一个不同的问题。抱歉贴了很多代码,不过就是长,不难读。

class Fruit {
    public int peel() {

            System.out.println("Peeling is appealing.");
            return 1;
        }
    }

class Apple extends Fruit {
}

class Example1 {

    public static void main(String[] args) {

        Apple apple = new Apple();
        int pieces = apple.peel();
    }
}

修改后就不行了:

class Peel {

    private int peelCount;   
    public Peel(int peelCount) {
        this.peelCount = peelCount;
    }    
    public int getPeelCount() {    
        return peelCount;
    }
}

修改 Fruit 类会导致编译错误代码。

class Peel {

    private int peelCount;

    public Peel(int peelCount) {
        this.peelCount = peelCount;
    }

    public int getPeelCount() {

        return peelCount;
    }
}

class Fruit {

// Return a Peel object that
// results from the peeling activity.
    public Peel peel() {

        System.out.println("Peeling is appealing.");
        return new Peel(1);
    }
}

旧的实现已损坏。这个问题可以通过组合来解决:

class Peel {

    private int peelCount;

    public Peel(int peelCount) {
        this.peelCount = peelCount;
    }

    public int getPeelCount() {

        return peelCount;
    }
}


class Fruit {

// Return int number of pieces of peel that
// resulted from the peeling activity.
public Peel peel() {

System.out.println("Peeling is appealing.");
        return new Peel(1);
    }
}

// Apple must be changed to accomodate
// the change to Fruit
class Apple {

    private Fruit fruit = new Fruit();

    public int peel() {

        Peel peel = fruit.peel();
        return peel.getPeelCount();
    }
}

// This old implementation of Example2
// still works fine.
class Example1 {

    public static void main(String[] args) {

        Apple apple = new Apple();
        int pieces = apple.peel();
    }
}

随着构图的风格,苹果不再是-与水果的关系。它变成了一个has-a。 Fruit 中的方法作为继承委托给 Apple。我的问题出现了:

  • 使用包装器方法从 Fruit 委托方法,这不是促进复制和粘贴,这是一种不好的做法吗?如果我们在 Fruit 中有大约 10-50 种方法,则为图像。如果有大约 50 个子类要继承,怎么可能通过组合来继承?

  • 关于第一个例子,使用扩展继承,作者建议超类中的一次更改将破坏使用它的实现。但是,我想知道,为什么需要更改它? OO原则之一不是“关闭修改,打开扩展”吗?在作者所说的情况下,我仍然可以保留旧的超类实现并使其适应新的变化。像这样:

    类水果{

    // Return a Peel object that
    // results from the peeling activity.
    private Peel getPeel() {
    
        System.out.println("Peeling is appealing.");
        return new Peel(1);
    }
    public int peel(){
        Peel peel = getPeel();
        return peel.getPeelCount();
    }
    

    }

向超类添加新方法以适应而不是更改或替换旧方法是否有问题,这会破坏程序结构?据我所知,通过这样做,我仍然可以实现与组合示例相同的事情,并且我不必制作很多包装方法来为每个子类委托超类的方法。

  • 由于上述原因,我自己总结认为,只有当有多重继承时,或者当我需要共享不同且不属于一个类族的行为时,才使用组合更好。例如:

界面健谈{

 public void talk();

}

Animal 类族可以实现 Talkative 接口以及 Person 类族。你觉得我的结论怎么样?

【问题讨论】:

    标签: java design-patterns


    【解决方案1】:

    基本上,您似乎试图通过创建包装类来避免更改依赖于旧 API 的客户端。

    不幸的是,这不是很好。它导致包装器类的激增,以及创建包装器类的实例的其他麻烦。如果您这样做,您的代码库/应用程序会变得臃肿,性能会受到影响,并且您的代码会变得越来越难以理解。

    更好的方法是:

    • 首先尝试正确使用 API。 (呃!)
    • 如果 API 更改没有增加显着价值或解决重要问题,请推迟进行。
    • 当您确实需要更改 API 时,请以二进制兼容的方式进行;例如添加新方法,而不是对现有方法进行二进制不兼容的更改。如果您打算摆脱旧方法,请将其标记为已弃用。
    • 当您必须更改二进制不兼容的 API 时,请同时更改所有使用它的代码。

    @Deprecated 标记允许您向人们发出 API 警告,警告不应再使用某个方法,同时为他们提供一个窗口来修复他们的代码以使用替换方法/替代方法。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-01-17
      • 2015-03-07
      • 1970-01-01
      • 1970-01-01
      • 2012-06-17
      • 2016-01-05
      • 2014-08-08
      相关资源
      最近更新 更多