返回介绍

16.4.1 制作更多的对象

发布于 2024-10-15 23:56:35 字数 2902 浏览 0 评论 0 收藏 0

这样便引出了面向对象程序设计时一条常规的准则,我最早是在 Grady Booch 那里听说的:“若设计过于复杂,就制作更多的对象”。尽管听起来有些暧昧,且简单得可笑,但这确实是我知道的最有用一条准则(大家以后会注意到“制作更多的对象”经常等同于“添加另一个层次的迂回”)。一般情况下,如果发现一个地方充斥着大量繁复的代码,就需要考虑什么类能使它显得清爽一些。用这种方式整理系统,往往会得到一个更好的结构,也使程序更加灵活。

首先考虑 Trash 对象首次创建的地方,这是 main() 里的一个 switch 语句:

    for(int i = 0; i < 30; i++)
      switch((int)(Math.random() * 3)) {
        case 0 :
          bin.addElement(new
            Aluminum(Math.random() * 100));
          break;
        case 1 :
          bin.addElement(new
            Paper(Math.random() * 100));
          break;
        case 2 :
          bin.addElement(new
            Glass(Math.random() * 100));
      }

这些代码显然“过于复杂”,也是新类型加入时必须改动代码的场所之一。如果经常都要加入新类型,那么更好的方案就是建立一个独立的方法,用它获取所有必需的信息,并创建一个句柄,指向正确类型的一个对象——已经上溯造型到一个 Trash 对象。在《Design Patterns》中,它被粗略地称呼为“创建范式”。要在这里应用的特殊范式是 Factory 方法的一种变体。在这里,Factory 方法属于 Trash 的一名 static(静态)成员。但更常见的一种情况是:它属于衍生类中一个被过载的方法。

Factory 方法的基本原理是我们将创建对象所需的基本信息传递给它,然后返回并等候句柄(已经上溯造型至基础类型)作为返回值出现。从这时开始,就可以按多形性的方式对待对象了。因此,我们根本没必要知道所创建对象的准确类型是什么。事实上,Factory 方法会把自己隐藏起来,我们是看不见它的。这样做可防止不慎的误用。如果想在没有多形性的前提下使用对象,必须明确地使用 RTTI 和指定造型。

但仍然存在一个小问题,特别是在基础类中使用更复杂的方法(不是在这里展示的那种),且在衍生类里过载(覆盖)了它的前提下。如果在衍生类里请求的信息要求更多或者不同的参数,那么该怎么办呢?“创建更多的对象”解决了这个问题。为实现 Factory 方法,Trash 类使用了一个新的方法,名为 factory。为了将创建数据隐藏起来,我们用一个名为 Info 的新类包含 factory 方法创建适当的 Trash 对象时需要的全部信息。下面是 Info 一种简单的实现方式:

class Info {
  int type;
  // Must change this to add another type:
  static final int MAX_NUM = 4;
  double data;
  Info(int typeNum, double dat) {
    type = typeNum % MAX_NUM;
    data = dat;
  }
}

Info 对象唯一的任务就是容纳用于 factory() 方法的信息。现在,假如出现了一种特殊情况,factory() 需要更多或者不同的信息来新建一种类型的 Trash 对象,那么再也不需要改动 factory() 了。通过添加新的数据和构建器,我们可以修改 Info 类,或者采用子类处理更典型的面向对象形式。

用于这个简单示例的 factory() 方法如下:

  static Trash factory(Info i) {
    switch(i.type) {
      default: // To quiet the compiler
      case 0:
        return new Aluminum(i.data);
      case 1:
        return new Paper(i.data);
      case 2:
        return new Glass(i.data);
      // Two lines here:
      case 3: 
        return new Cardboard(i.data);
    }
  }

在这里,对象的准确类型很容易即可判断出来。但我们可以设想一些更复杂的情况,factory() 将采用一种复杂的算法。无论如何,现在的关键是它已隐藏到某个地方,而且我们在添加新类型时知道去那个地方。

新对象在 main() 中的创建现在变得非常简单和清爽:

    for(int i = 0; i < 30; i++)
      bin.addElement(
        Trash.factory(
          new Info(
            (int)(Math.random() * Info.MAX_NUM),
            Math.random() * 100)));

我们在这里创建了一个 Info 对象,用于将数据传入 factory();后者在内存堆中创建某种 Trash 对象,并返回添加到 Vector bin 内的句柄。当然,如果改变了参数的数量及类型,仍然需要修改这个语句。但假如 Info 对象的创建是自动进行的,也可以避免那个麻烦。例如,可将参数的一个 Vector 传递到 Info 对象的构建器中(或直接传入一个 factory() 调用)。这要求在运行期间对参数(自变量)进行分析与检查,但确实提供了非常高的灵活程度。

大家从这个代码可看出 Factory 要负责解决的“领头变化”问题:如果向系统添加了新类型(发生了变化),唯一需要修改的代码在 Factory 内部,所以 Factory 将那种变化的影响隔离出来了。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文