【问题标题】:What is composition as it relates to object oriented design?什么是与面向对象设计相关的组合?
【发布时间】:2011-03-27 08:19:27
【问题描述】:

我听到(并在此网站上阅读)很多关于“偏好组合优于继承”的信息。

但是什么是合成?我从 Person : Mammal : Animal 的角度理解继承,但我在任何地方都看不到 Compostion 的定义。有人可以填写吗?

【问题讨论】:

    标签: oop design-patterns language-agnostic composition


    【解决方案1】:

    组合是指将简单的类型组合成更复杂的类型。在您的示例中,组合可能是:

    Animal:
        Skin animalSkin
        Organs animalOrgans
    
    
    Mammal::Animal: 
        Hair/fur mammalFur
        warm-blooded-based_cirulation_system heartAndStuff
    
    Person::Mammal: 
        string firstName
        string lastName
    

    如果你想完全组合(并摆脱所有继承),它看起来像这样:

    Animal:
        Skin animalSkin
        Organs animalOrgans
    
    Mammal:
        private Animal _animalRef
        Hair/fur mammalFur
        warm-blooded-based_cirulation_system heartAndStuff
    
    Person:
        private Mammal _mammalRef
        string firstName
        string lastName
    

    这种方法的优点是MammalPerson 类型不必符合其先前父级的接口。这可能是一件好事,因为有时对超类的更改会对子类产生严重影响。 他们仍然可以通过这些类的私有实例访问这些类的属性和行为,如果他们想公开这些以前的超类行为,他们可以简单地将它们包装在一个公共方法中。

    我在这里找到了一个很好的示例链接:http://www.artima.com/designtechniques/compoinh.html

    【讨论】:

    • 所以我可以这么说:“组合是当我在 Class B 中创建 Class A 的对象时(而不是从 Class A 子类化 Class B)。” ?
    • 哦。好的,知道了w69rdy 稍后回答。是的,我可以这么说。
    【解决方案2】:

    组合只是构成整体的部分。汽车有轮子、发动机和座椅。继承是一种“是”的关系。组合是一种“具有”关系。

    【讨论】:

    • 而聚合是...的关系。
    • 聚合可能是简单的组合,或者如果它是类似事物的集合(例如,汽车上的轮子),它可能被视为一个集合。一辆车可以有四个单独的轮子,唯一标识,也可以有一组轮子。这取决于使用情况。如果使用集合类,那么集合本身就是一个聚合。
    【解决方案3】:

    可以通过三种方式为类赋予行为。您可以将该行为写入类中;您可以从具有所需行为的类继承;或者您可以将具有所需行为的类作为字段或成员变量合并到您的类中。最后两种代表代码重用的形式,最后一种——组合——通常是首选。它实际上并没有为您的类提供所需的行为 - 您仍然需要在该字段上调用该方法 - 但它对您的类设计的约束更少,并且更容易测试和调试代码。继承有它的位置,但组合应该是首选。

    【讨论】:

      【解决方案4】:
      class Engine
      {
      
      }
      
      class Automobile
      {
      
      }
      
      
      class Car extends Automobile // car "is a" automobile //inheritance here
      { 
       Engine engine; // car "has a" engine //composition here
      
      }
      

      组合 - 对象的功能由不同类的集合组成。在实践中,这意味着持有指向另一个工作被推迟的类的指针。

      继承 - 对象的功能由它自己的功能加上其父类的功能组成。

      关于为什么组合优先于继承,请看Circle-ellipse problem

      【讨论】:

        【解决方案5】:

        组合的一个例子是你在另一个类中拥有一个类的实例,而不是从它继承

        This 页面有一篇很好的文章解释了为什么人们说“喜欢组合而不是继承”,并举例说明了原因。

        【讨论】:

        • 不是真正的另一个类[C1]中的类[C2]的实例,而是实例 >另一个类的实例。前者可能会被误解为在定义 C1 时实例化了 C2,这不应该是常见的。
        【解决方案6】:

        作曲

        仅仅意味着使用引用其他对象的实例变量。


        为了说明在代码重用部门中继承与组合的比较,请考虑以下非常简单的示例:

        1- 通过继承编写代码

            class Fruit {
        
            // Return int number of pieces of peel that
            // resulted from the peeling activity.
            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();
            }
        }
        

        当您运行Example1 application 时,它会打印出“Peeling is有吸引力。”,因为Apple 继承(重用)Fruit 对peel() 的实现。但是,如果在将来的某个时间点,您希望将peel() 的返回值更改为Peel,您将破坏Example1 的代码。即使 Example1 直接使用 Apple 并且从未明确提及 Fruit,您对 Fruit 的更改也会破坏 Example1 的代码。 更多信息参考 这就是它的样子:

        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);
            }
        }
        
        // Apple still compiles and works fine
        class Apple extends Fruit {
        }
        
        // This old implementation of Example1
        // is broken and won't compile.
        class Example1 {
        
            public static void main(String[] args) {
        
                Apple apple = new Apple();
                int pieces = apple.peel();
            }
        }
        

        2- 通过组合编写代码 组合为Apple 重用Fruit's 实现peel() 提供了另一种方式。除了扩展FruitApple 可以持有对Fruit 实例的引用并定义自己的peel() 方法,该方法简单地调用Fruit 上的peel()。代码如下:

        class Fruit {
        
            // Return int number of pieces of peel that
            // resulted from the peeling activity.
            public int peel() {
        
                System.out.println("Peeling is appealing.");
                return 1;
            }
        }
        
        class Apple {
        
            private Fruit fruit = new Fruit();
        
            public int peel() {
                return fruit.peel();
            }
        }
        
        class Example2 {
        
            public static void main(String[] args) {
        
                Apple apple = new Apple();
                int pieces = apple.peel();
            }
        }
        

        更多信息ref

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-02-26
          • 2021-05-31
          • 1970-01-01
          • 2010-10-11
          • 1970-01-01
          相关资源
          最近更新 更多