【问题标题】:How to reduce code duplication with private variables setting without using inheritance?如何在不使用继承的情况下通过私有变量设置减少代码重复?
【发布时间】:2015-09-17 09:59:51
【问题描述】:

我有 MonthTimeCard 和 MonthReport 课程。它们使用相同的输入字段来选择月份,因此验证相同,初始化相同。

class MonthTimeCard {

private function setPrivateVarsByUserInput()
    {
        $this->month_from_input = '2015-09';

        $this->date_from = '2015-09-01';
        $this->date_to = '2015-09-30';

        // lets say those 3 values are formated, validated and returned as array from MonthUserInput class.
        // but I have to repeat code in my both classes to initialize them
        // I could have one liner like $this->data = $this->monthUserInput->getInput();
        // but current class code will need to call $this->data->[variable] everywhere - not looking very nice,
        // because ->data is not usefull here, just extra characters.
    }

}

Class MonthReport {
    ... same initialisation
}

我现在只是对它们进行了硬编码,但您知道分配将来自将验证的 MonthUserInput 类的返回数组。只有一个用户输入字段可以获取像“2015-09”这样的字符串,MonthUserInput 类将添加月份的第一天和最后一天。

如果我使用继承,我可以避免这种情况,但我读过应该避免这种情况,甚至有人说它应该从 OOP 中删除。

http://blogs.perl.org/users/sid_burn/2014/03/inheritance-is-bad-code-reuse-part-1.html

通过继承,我只需在父类中验证和设置用户输入,因此没有重复。

我现在应该怎么做?当然,在这个例子中它只有 3 个变量,我打算有 5 个,虽然不多,但仍然是重复的,在这种情况下,继承不会有重复。

更新

我希望看到可以用来向任何不同意继承对这种情况不利的人解释的解释。例如,我与一位经验丰富的程序员讨论过,他认为在这里使用继承来重用代码没有问题。我只是说网上有很多关于使用组合的说法,但这不是这种情况的好论据。

例如:

优先组合而不是继承。如果两个类没有“是 a" 关系,那么永远不需要使用继承 实现代码复用。从不。

这对某些人来说不是一个好的论据。他们可以问 - 谁发明了这些规则,为什么?他们没有看到违反这条规则的问题。

【问题讨论】:

  • 这是什么语言? Perl,PHP?请在您的帖子中添加适当的标签。
  • 那篇博文很烦人,因为他创建了一个简单的模型,然后更改了需求并说“哦,看,模型坏了!”。然后他创建了自己的模型,但首先说明了所有要求。他声称他的建模技术非常出色,因此继承很糟糕。
  • @user1438038 - 我正在使用 PHP,但它应该适合任何具有 OOP 的语言

标签: php perl oop code-duplication


【解决方案1】:

继承是一个强大的工具,可以实现代码重用,但是它本身也带来了潜在的问题,应该适当使用。因为子类依赖超类实现,继承破坏了封装并且会使代码更加脆弱和难以维护。

实现规则在确实存在“IS A”关系时使用继承,例如。 PreferredClient 是客户……或…… MountainBike 是自行车。如果您正在设计的类无法与预期的超类实现 IS A 关系,则不要使用继承。

您可以通过使用组合而不是继承来实现类似程度的代码重用。与其创建子类,不如将预期的超类合并为类的属性。这允许您将方法调用委托给包含的实例并实现代码重用。

对于您的代码示例,建议的代码分解可能是这样的:

class MonthTimeCard {

    Month myMonth;

    MonthTimeCard(Month month) { myMonth = month; }

    private function setPrivateVarsByUserInput()  {
        // calls to myMonth
    }

}

Class MonthReport {
    // calls to myMonth
}

Class Month {
    $this->month_from_input = ...

    $this->date_from = ...
    $this->date_to = ...
}

【讨论】:

  • 我在想你给的方式。但是,我如何从 MonthTimeCard 直接或通过 getter 函数访问 Month 中的私有变量?然后我将不得不在我的 MonthTimeCard 代码中的任何地方使用 $this->month->getDateFrom(),类似于我给出的 $this->data->date_from 示例,这看起来很奇怪,但如果这是最好的解决方案,那么我能做到。
  • 我也可以通过一些花哨或创造性的命名来强制 is-a 关系,MonthTimeCard 是 MonthWorkData :D 但可能这并不好。或者也许是?根据我几年的经验,我不记得继承使代码难以维护的情况。
  • 将类的名称更改为似是而非地类似于“IS A”关系是一种表面上遵守规则而忽略规则精神的方法。 A 类要么是 B,要么不是(不管它的名字是什么)。
  • “但是我如何从 MonthTimeCard 访问 Month 中的私有变量” ...实例字段必须始终是私有的(除了少数例外)。它们是你的类的实现细节,你应该让它们对其他类隐藏。使用来自其他对象的访问器方法来获取您的类的数据。
  • 在这种情况下我不需要更改名称,但是如果我要使用这种方法,我需要创建父类。
【解决方案2】:

如果MonthTimeCardMonthReport 在逻辑上不能相互扩展(例如DogCatAnimal 的子类型),那么无论如何在这里使用继承都不是一个好主意。

此外,如果您在多个位置进行相同的验证,您可以引入一个独立的 DateValidator 类并在需要时使用它。

public class DateValidator {

  public static boolean validate(final String dateString) {
    return ...; // TODO: Validate user input here
  }

}

【讨论】:

  • 它们不会相互扩展,它们会扩展另一个类,例如 Dog 扩展 Animal。在 Animal 类中,我会进行验证和私有变量初始化。在您的示例中 - 验证是可以的,但问题是设置多个变量,这些变量的值是使用完全相同的逻辑从用户输入生成的。
  • 如果类从逻辑角度看没有共同点,不要让它们扩展同一个基类。初始化是按实例完成的,因此通常您不希望共享初始化代码。如果你真的有很多属性并且想要简化初始化,让自己熟悉Prototype Pattern。但根据您的要求,它不是正确的工具。坚持每个类都有自己的init() 方法,并让它们共享一个DateValidator 来解析用户输入。
猜你喜欢
  • 2018-07-08
  • 1970-01-01
  • 2013-03-10
  • 2017-09-07
  • 2012-10-31
  • 2021-10-27
  • 1970-01-01
  • 2017-04-08
  • 2017-01-25
相关资源
最近更新 更多