从父类调用子类方法

3
我是一个有用的助手,可以翻译文本。
我有一个类被多个其他类使用作为扩展程序,在某些情况下,父类中的一个方法需要回调到子类中的一个方法。有没有一种方法可以实现这个功能?
我知道PHP包含抽象类和函数,但这需要每个子类都声明抽象函数,而在这种情况下我并不需要。
例如(这些只是示例,不是真实的)-
Class parent{

    function on_save_changes(){

        some_parent_function();

        if($_POST['condition'] === 'A') :
            // Call 'child_1_action()'
        elseif($_POST['condition'] === 'B') :
            // Call 'child_2_action()'
        endif       

        some_other_parent_function();

    }

    function some_parent_function(){
        // Do something here, required by multiple child Classes
    }

}

Class child_1 Extends parent{

    function __construct(){
        $this->on_save_changes();
    }

    function child_1_action(){
        // Do something here, only every required by this child Class
    }

}

Class child_2 Extends parent{

    function __construct(){
        $this->on_save_changes();
    }

    function child_2_action(){
        // Do something here, only every required by this child Class
    }

}

2
有另一个在stackoverflow上的线程与相同的查询:https://dev59.com/mHI-5IYBdhLWcg3wQV8Z - maxxon15
请看一下我问题的第二段,那里解释了为什么在这种情况下,那个答案对我来说不正确。 - David Gard
你可以在父类中添加一个存根方法的定义,该方法什么也不做,但允许子类覆盖它。 - Jerry
3个回答

7
你可以通过简单调用子方法来实现这一点,例如:

只需这样做:

if($_POST['condition'] === 'A') :
    $this->some_parent_function();
    $this->child_1_action();
然而,你应该避免这样做。 在父类中放置调用仅存在于子类中的方法的检查是一种非常糟糕的设计味道。通过利用众所周知的设计模式或者更好地思考类层次结构,总有一种更加结构化的方法来完成事情。
一个非常简单的解决方案是在父类中实现所有这些方法作为无操作函数;每个子类可以覆盖(并提供实现)它感兴趣的方法。虽然这是一种有些机械的解决方案,因此无法确定它是否是您的最佳选择,但即使如此,它也比直接调用技术上不能保证存在的方法要好得多。

谢谢您的回复。直接从父类调用子类方法是行不通的(会出现「未定义的方法调用」错误)。我想,在这种情况下,我会重写「on_save_changes()」方法。一开始需要重复一些功能,但我很感激您说的更好地构造类结构的观点。 - David Gard
@DavidGard:如果你遇到这样的错误,意味着你正在尝试在错误类型的子对象上调用方法。虽然这绝对是调用逻辑中的一个错误(或者是子类应该实现但没有实现的方法),可以修复,但这也正是为什么你一开始就不应该这样做的确切原因。 - Jon

1

试试这个:

class ParentClass{

    private $childActionMethod;

    public function on_save_changes(){
        if(method_exists($this, $this->childActionMethod)) {
            call_user_func_array(array($this, $this->childActionMethod), func_get_args());
        }
        else {
            throw new Exception('Child Method has not been set');
        }
    }

    protected function setChildActionMethod($methodName) {
        $this->childActionMethod = $methodName;
    }
}

class ChildClass1 extends ParentClass{

    function __construct(){
        $this->setChildActionMethod('child_1_action');
    }

    function child_1_action(){
        echo('Hello First World<br />');
    }
}

class ChildClass2 extends ParentClass{

    function __construct(){
        $this->setChildActionMethod('child_2_action');
    }

    function child_2_action(){
        echo('Hello Second World<br />');
    }
}

$child1 = new ChildClass1();
$child1->on_save_changes();
// Hello First World

$child2 = new ChildClass2();
$child2->on_save_changes();
// Hello Second World

父类有一个受保护的方法setChildActionMethod,子类可以调用。当子类被实例化时,它们会告诉父类它们想要在保存时调用的方法名称。
如果该方法存在,则使用任何参数调用它,否则会抛出异常(您可以更改错误处理方式)。
我确定这种模式有一个名称,但我不确定它叫什么。

有趣的方法。虽然从技术上讲,我认为那样做是可行的,但如果可能的话,我更愿意避免,因为它有点凌乱。不过还是谢谢你的建议。 - David Gard
你不会找到一个干净的解决方案,父类不应该知道任何关于派生类的信息,除了它自己声明的属性和方法的“存在”。 - Flosculus

0
如果您需要在父类中创建一些子类应以某种预定义的方式实现的操作序列,但不应引用未来定义的任意方法,则可以使用“模板方法”模式。
通常情况下:您在父类中使用的任何方法都应声明为abstract或具有默认实现。子类将覆盖这些方法。
abstract class parent{
  function on_save_changes(){
    some_parent_function();
    some_child_action();
    some_other_parent_function(); // added to follow changes of question
  }

  function some_parent_function(){
    // Do something here, required by multiple child Classes
  }

  abstract public function some_child_action();

}

class child_1 Extends parent{
  function some_child__action(){
    if($_POST['condition'] === 'A') : 
      // Do something here, only every required by this child Class
    endif;
   }
}

class child_2 Extends parent{
  function some_child_action(){
    if($_POST['condition'] === 'B') :
      // Do something here, only every required by this child Class
    endif; 
  }
}

但是我应该注意到,即使在这种情况下,由于将类层次结构与显式分支混合在一起,这也是不好的设计。 - dmitry
谢谢回复。所以在这种情况下,我需要去“child->parent->child->parent”来获得我需要的功能吗?从技术上讲,这样做是可行的,所以我会考虑一下这个技巧... - David Gard
你理解什么是“抽象”方法吗?只是为了确保。你创建一个模板(on_save_changes),其中包含一些逻辑框架,所有你无法在父类中实现的方法都可以被定义为抽象方法,并留待子类实现。这就是“模板方法”模式。 - dmitry
我相信是的,尽管我自由承认在这个主题上不是专家(因此提出问题)。我刚刚对我的问题进行了编辑,实际上使我的最初评论有些无效。我将不得不进行一些重组以使其工作,但说实话,我认为这在这一点上并不是坏事。 - David Gard
如果您提供一些上下文描述您的任务,那么这可能会有所帮助。程序设计是计算机科学中最敏感和最困难的学科,因此总有学习新知识的空间。糟糕的设计决策比仅仅出现编程错误(如错误的数据结构或类型)更糟糕,所以请三思而后行 :) - dmitry

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