特质 vs 抽象类:代码复用的选择

3
在做项目时,我遇到了一个与选择正确的代码重用形式有关的问题,涉及到特质和抽象基类。我遇到了这样一个情况,我有多个抽象基类,但由于PHP的多重继承限制,只能从一个基类继承。重新思考这一点让我对我的设计产生了疑问,因此我想知道其他人如何解决这个问题。
以下是场景:
在我的项目中,我有两种计算:
1.简单计算
2.分组计算,这种情况下只是带有一些更多信息的简单计算
接下来是设置接口。
interface SimpleResultInterface{
    public function getValue();
}

interface GroupedResultInterface extends SimpleResultInterface {
    public function getGroupValues();
}

设置基类

为了避免代码重复,作为一种良好的设计形式,我设置了两个抽象基类。

abstract class SimpleResultBase implements SimpleResultInterface{

    public function getValue(){
        return 1;
    }
}

abstract class GroupedResultBase extends SimpleResultBase implements  GroupedResultInterface {

    public function getGroupValues(){
        return array(
            "group1" => 1,
            "group2" => 2,
            "group3" => 3,
        );
    }
}

所以这里的第一个问题是:
1. 从 SimpleResultInterfaceGroupedResultInterface 的接口关系来表达,GroupedResultBase 是否需要继承 SimpleResultBase
我选择是,因为否则就需要复制 SimpleResultBasegetValue 方法。
现在我们可以表达:
1. GroupedResultInterface instanceof SimpleResultInterface (true) 2. GroupedResultBase instanceof SimpleResultBase (true) 3. SimpleResultBase instanceof SimpleResultInterface (true) 4. GroupedResultBase instanceof GroupedResultInterface (true)
接下来要开始具体实现时会遇到麻烦:
在这一点上没有具体类。现在我想要创建两个具体类,它们之间有一个关系,遵守接口定义。
class OEESimpleResult extends SimpleResultBase {

}

class OEEGroupedResult extends GroupedResultBase{

}

这很好用,但我无法做到这一点。
class OEESimpleResult extends SimpleResultBase {

}

class OEEGroupedResult extends OEESimpleResult,GroupedResultBase {

}

interface GroupedResultInterface 继承自 SimpleResultInterface

正如在接口定义中所述,每个分组的结果也是一个简单的结果。因此,我希望 OEEGroupedResult 也是一个 OEESimpleResult

但是如果这样做,我就无法重用我的抽象基类 GroupedResultBase,因为不允许多重继承。这引发了以下问题:

  1. 看起来,抽象类中的类关系会导致继承链路冻结。是否最好删除抽象类并用 trait 替换它们,以便使用它们的具体类?
  2. 如果您有多个接口但只能继承一个基类,则提供默认实现的正确方法是什么?
  3. 这个问题在面向对象编程中似乎非常普遍,是否有任何最佳实践和建议来解决此问题?
  4. 实现接口的类是否应该像接口级别那样表达继承关系,还是仅凭借接口关系就足够了?在我的情况下,OEEGroupedResult 是否应该像它们的接口那样在类级别上表达与 OEESimpleResult 的关系?

感谢您的帮助 :)


1
你能回复一下吗?因为你的问题确实值得讨论。我的答案并不是唯一的方法,所以我们可以一起改进它。但是如果你想得到答案,忽略它并不是最好的决定。 - jaro1989
这不是唯一的方法,但你的答案让我找到了正确的解决方案! - Jay
1个回答

2

作为一个使用面向对象编程(oop)风格的开发者,你可能已经听说过SOLID原则。这就是说,你的设计有可能会破坏S、I、D三个原则,并有潜力影响O和L。

那么,如何破坏“S”原则呢? 就在这里!

class OEEGroupedResult extends OEESimpleResult,GroupedResultBase {

}

突然或幸运的是,PHP不允许这样做,但您的类将提供一些额外的独立逻辑,因此您至少有两个更改它的理由。但是,是的,这是因为

interface GroupedResultInterface extends SimpleResultInterface {
    public function getGroupValues();
}

这是您的I字母断开问题。解决方案非常简单:考虑您的GroupedResultInterface真正需要的方法,并将其放在内部,确保这些方法执行相同的工作。它们不像您在此处打断字母D一样:
abstract class SimpleResultBase implements SimpleResultInterface{

    public function getValue(){
        return 1;
    }
}

abstract class GroupedResultBase extends SimpleResultBase implements  GroupedResultInterface {

    public function getGroupValues(){
        return array(
            "group1" => 1,
            "group2" => 2,
            "group3" => 3,
        );
    }
}

你的类 class GroupedResultBase 依赖于另一个类的具体方法。但是在接口隔离后,你将很快解决这个问题。
注意:当我无法更改供应商类中的某些内容时,我会使用 traits。只有这种情况下才会这样做。当我认为 trait 会帮助我的时候,我觉得架构可能存在问题。当然,这取决于你。
小链接:https://en.wikipedia.org/wiki/SOLID_(object-oriented_design) 玩得开心!

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接