我怎样才能继承Perl中的具体类?

时间:2009-11-06 04:01:07

标签: perl inheritance oop

我们的软件将每个设备表示为具体类。所以假设我有一个名为Cpu-Version-1的类,它来自一个名为Device的抽象基类。现在,CPU供应商想要发布这个CPU的经济版本(Cpu-Version-2),这是Cpu-Version-1的功能缩减版本(显然价格较低)。 Cpu-Version-2的90%代码与Cpu-Version-1的代码相同,但剩余的10%代码不再相同。即这些是Cpu-Version-2中已删除的功能,因此我不再代表它。

现在设计这门课的最佳方法是什么?我可以从Cpu-Version-2继承Cpu-Version-1(即使继承不完美,因为这两个设备都是对等的)。这将迫使我覆盖cpu-version-1的许多方法什么都不做(看起来很丑陋,不知何故感觉不对)。此外,我认为我不能更改基类(Cpu-Version-1或它的基础),因为代码已经在生产中并且需要大量的批准和理由。如果你是我,你会如何做出这个设计决定?

我们如何使Cpu-Version-2具有可重用性和可维护性?你会遵循任何OOP原则吗?或者,采用简单的方式并覆盖Cpu-Version-1的所有不适用的方法,什么都不做,这是更好的权衡吗?我在面向对象的Perl中编码。

4 个答案:

答案 0 :(得分:4)

我认为阻碍面向对象编程项目进展的主要障碍之一是常见的误解,即继承是从多个来源组成新类的唯一方式。但是一般来说,继承实际上是一个非常糟糕的模型,并且只适用于可以在完美的层次结构中整齐地分类的事物。正如您所发现的,现实世界通常不会那样工作。

听起来遗传不是这个问题的好模型。幸运的是,还有其他模型可用于在Perl中构造类。

使用Moose,您可以使用roles组合具有“执行”功能组合的类,而无需创建复杂的(在这种情况下,不适合)继承层次结构。 / p>

如果你不想要像Moose那样重要的东西,那么还有更简单的选项,如mixin

您可能感兴趣的另一个选项是基于原型的合成,与Class::Prototyped一样。这仍然是基于层次结构的,但是通过允许您混淆单个实例然后将它们用作新实例的原型,可以为您提供更大的灵活性。这就是OO在Javascript和Self等语言中的表现。

答案 1 :(得分:3)

如果你不能一直使用Moose和角色,你可以重构CPU类以使用组合委托

CPU1 is a CPU
CPU1 has a Frobnicator
CPU1 has a Thingummy
CPU1 has a Poddlewhick
CPU1 has a Fridgemagnet

Frobnicator,Thingummy和Poddlewhick都是在CPU对象上运行的类。

CPU2 is a CPU
CPU2 has a Frobnicator
CPU2 has a Thingummy
CPU2 has a Poddlewhick

CPU2就像CPU1一样,但没有Fridgemagnet。

组合类的方法需要获取传递给它们的CPU对象的副本,以便它们可以从委托者请求信息。

package CPU;

sub delegate {
    my $self     = shift;
    my $accessor = shift;
    my $method   = shift;

    return $self->$accessor->$method( $self, @_ );
}

package CPU1;

use Frobnicator;
use Thingummy;
use Poddlewhick;
use Fridgemagnet;


package CPU2;

use Frobnicator;    
use Thingummy;
use Poddlewhick;


package Frobnicator;

use Exporter;
our @ISA = /Exporter/;

our @EXPORT_OK = qw( frobnicate );
our @EXPORT = @EXPORT_OK;

sub frobnicate {
    my $delegator = shift;

    return $delegator->delegate( frobnicator => frob => @_ );
}

这种方法会导致很多重复的代码。您可以使用import语句通过use函数将一些伪角色类型代码注入到您的类中。但是,如果你需要走得那么远,只需使用Moose而不是重新实现它。

仅在您不能将Moose与角色一起使用时才执行此操作。它们工作得更好,麻烦和样板更少。

答案 2 :(得分:2)

您有几种选择。我不认为CPU-Version-2派生自CPU-Version-1就是答案。

如果您的代码是“可重构的”(即您有足够的单元测试可以让您自信地进行重构),那么您可以考虑创建一个直接从Device继承的CPU类。所有常用方法都可以移到CPU,然后你可以从那里派生出CPU-Version-X。

然而,更好的是,可能会使用构图。在CPU-Version-2对象中保留CPU-Version-1的实例。对于任何常用方法,您的CPU-Version-2对象可以简单地委托给CPU-Version-1中的等效方法。您可以使用CPU-Version-2自己的方法来增强此行为,以满足要求。

例如(伪代码):

class CPU-Version-2:
   CPU-Version-1 cpu;

   int commonMethod1():
      return cpu.method1();

   void commonMethod2():
      cpu.doSomething();

   int newMethod():
      x = cpu.getSomeValue();
      y = x + 42;
      return y;

这有帮助吗?

答案 3 :(得分:1)

听起来你需要将更多的东西移动到抽象类中,这样你就可以使用他们需要的具体类来创建具体的类,并且每个类都继承自抽象类。

或者,既然你说你不能改变基类(那么,去不可重复使用且不可维护的路线),你可以在Cpu-Version-2中找到东西。如果您继承自Cpu-Version-1但不想要其中一种方法,则将其覆盖为什么都不做:

 package CPU::V2;
 use parent qw( CPU::V1 );

 sub dont_need_this_feature { return }

没有看到实际的课程以及你做了什么,这就像我能给你的那样好。