3

假设我有一个List<Apple>对象,每个苹果都有一种颜色。

我实现了另一个对象,该对象是用其输入苹果对象列表构造的。我可能会在这个对象上实现一些功能,比如“让我知道青苹果的数量。”,我可以在不知道该对象的内部表示的情况下调用它。

你会怎么称呼这个人?这似乎是基本的 OO,但我在想一个描述性的名称时遇到了麻烦。

4

1 回答 1

0

由于您在谈论一种List<Apple>可能性,因此您也在谈论一种Collection<Apple>(列表继承集合)。

如果您想强调“收藏”,那么我将其命名为“收藏”。苹果传统上以蒲式耳出售,但您不关心测量,所以我会使用更通用(不取决于大小)的术语“篮子”。

public class Basket {

   private int greenCount;

   public void addApple(Apple apple) {
     if (apple.isGreen()) {
        greenCount++;
     }
   }

   public int getGreenAppleCount() {
      return greenCount;
   }

}

这恰当地抓住了计算篮子中青苹果的责任。

另一方面,实用程序似乎将任务的责任与特定类型绑定的对象分开。例如,让我们看一个假设的AppleCounterUtil.

public class AppleCounterUtil {

    public int getCount(Collection<Apple> apples, AppleCondition condition) {
        int count = 0;
        for (Apple apple : apples) {
            if (condition.isSatisfied(apple)) {
               count++;
            }
        }
    }

}

现在我们有了一个很好的实用程序,它实际上不维护计数,但每次需要从可能不同的苹果列表中重新计算计数。

关键是后一个例子不是面向对象的,因为没有概念对象。声明没有对象的基本原理是因为包含该实用程序的类缺少状态。对象是数据密切相关的代码的集合。当您只有代码时,可以合理地论证您有一个名称空间函数而不是一个对象。

为了再次看到差异,让我们比较两个代码片段。使用我们的第一个“篮子”方法:

...
int greenOnes = basket.getGreenAppleCount();
basket.addApple(new GreenApple());
greenOnes = basket.getGreenAppleCount();
...

变得非常笨重,但是由于我们是面向对象的,我们可以很容易地添加一个Basket Listener接口到Basket

public class Basket {

   public void addListener(BasketListner listener) {
     ...
   }

   public void addApple(Apple apple) {
     ...
     for (BasketListnener listner : listeners) {
       listener.appleAdded(this);
     }
   }

}

... some other class ... implements BasketListener {

   int greenOnes = 0;

   ...

   public void appleAdded(Basket basket) {
      greenOnes = basket.getGreenAppleCount();
   }
}

而使用面向实用的技术

...
int greenOnes = AppleCounterUtil.getCount(apples, new GreenCountCondition());
apples.addApple(new GreenApple());
greenOnes = AppleCounterUtil.getCount(apples, new GreenCountCondition());
...

表面上看起来很相似,直到您尝试添加更多功能。在尝试添加“苹果列表监听器”时,您很快就会意识到它AppleCounterUtil不负责维护计数,并且无法被监听。“苹果列表”也不负责维护计数,通用列表通常会提供错误的监听接口。

不幸的是,当一个人变得面向实用程序时,他们通常会尝试通过添加更多实用程序方法来解决问题。这最终可能意味着特定的关注点(谁管理苹果组)可以分布在许多实用程序中,而无需任何实用程序单独负责该任务。每个实用程序只提供一些可以按需计算的“功能”。

在某些情况下,按需计算功能可能会依赖于其他按需计算功能,这样您就可以在两者之间获得功能耦合(即使没有直接的代码耦合)。这种有效的耦合意味着如果您未能按顺序调用特定的一组实用程序,或者省略了所需的实用程序调用,您的代码就会中断。一个极端的情况是对象最终失去了它们的行为,以至于它们变成了数据结构,或者失去了通常与对象相关联的行为。它是一种面向对象的设计反模式,被称为“贫血对象”。

于 2014-11-04T23:24:45.377 回答