构造器内为什么不能执行某个方法

2025-02-25 10:03:00
丁国栋
原创 75

原来如此

感悟名称 : 禅道框架错误使用之构造器内为什么不能使用display方法

感悟起源 : 禅道框架在开始处理请求时先通过初始化一个router类的实例并设置为全局变量$app,这可以用来调用不同类中的方法。这种设计类似面向对象编程中“继承”的“组合模式”(在当前类中创建其他类的实例供当前类或子类使用)将不同的类塞入到router中。作为程序员我们知道我们通过子类继承父类扩展父类实现自定义功能,因此为了对extension类增加功能去定义 myextension 类扩展 extension类(其中 extension 扩展了 control,control 扩展了 baseControl)。原计划是在extension的子类myextension的构造器内判断是否需要执行其他方法并渲染视图(MVC中的V),以便在执行自身方法时先将执行流程转到这个视图处理一些前置条件(例如必须通过验证才能继续访问),于是我在myextension子类的构造器内执行了$this->display方法以便渲染这个视图来处理前置条件。结果测试发现这个视图执行到某个方法时报错,提示在router的control变量($app->control)下找不到appendExtendForm方法。这说明我不应该在构造器里执行display这个方法,那么为什么不能在构造器内执行this->display呢?
思考轨迹 :

经过查看调用栈,发现视图中的一个类尝试执行全局变量 router类下的control变量对应的mytension类中的appendExtendForm方法(见下图一,源码参见这里),其中appendExtendForm方法来自myextension的父类(的父类),是可以被调用到的(is_callable)。查看代码发现$app->control这个变量是在实例化mytension类后操作的,而myextension类中尝试执行这个视图的方法时还未将自己绑定到$app->control这个变量的方法(见下图二,源码参见这里),所以报错。

因此最佳实践是不要在构造器内执行可能包含其他类中的方法,构造器内做的事情最好都是能在当前类完成的事情。除非你了解你在构造器内所调用的方法来自何处,否则尽量避免使用全局变量中的方法。


附:引用DeepSeek的回答

问:php构造器内调用其他类的方法需要注意什么?
答:构造函数中调用其他类的方法,这可能涉及到依赖管理、对象状态、异常处理等方面的问题。常见的注意事项可能包括循环依赖、未初始化的对象状态、方法可见性、异常处理导致构造失败,以及是否违反单一职责原则。
首先,依赖注入的问题。如果在构造函数里直接实例化其他类,可能会导致紧耦合,测试困难。应该考虑依赖注入,通过参数传入,这样更灵活,也符合SOLID原则。
然后是对象状态。在构造函数执行期间,当前对象可能还没有完全初始化,如果这时候调用其他类的方法,并且那些方法又回过来调用当前对象的方法,可能会导致不可预见的错误,比如访问未初始化的属性。
方法可见性也是需要考虑的。如果调用的其他类的方法不是public的,比如protected或private,那么在外部调用会导致错误。需要确保调用的方法有适当的访问权限。
异常处理方面,如果在构造函数中调用的方法可能抛出异常,必须捕获并处理,否则构造函数会失败,导致对象无法创建。这可能影响整个程序的流程,需要妥善处理。
另外,循环依赖是一个大问题。比如类A的构造函数调用类B的方法,而类B的构造函数又调用类A的方法,这样会导致无限循环或者栈溢出,必须避免这种情况。
还有单一职责原则。构造函数的主要职责是初始化对象,如果里面调用了太多外部类的方法,可能让构造函数变得臃肿,职责不单一,这时候可以考虑使用工厂模式或者将初始化逻辑拆分到其他方法中。
可能还要提到依赖注入容器或服务定位器模式,来管理类之间的依赖,而不是在构造函数中硬编码实例化。另外,使用设计模式如工厂或策略模式来解耦代码。
有没有其他需要注意的点?比如性能问题,或者在构造函数中进行过多的计算和操作,影响对象的创建速度。或者资源泄漏的问题,比如调用方法时打开了资源但没有正确释放,不过这可能不太常见。
还有,如果调用的其他类的方法依赖于某些全局状态或配置,这时候在构造函数中调用可能会引入隐藏的依赖,使得测试和维护困难,应该尽量避免。
突破点 :

  1. 加深了解了禅道框架对类、方法的处理逻辑。
  2. 搞清楚了扩展某个方法的最佳实践。
适用场景 :
  1. 编写扩展时如何正确地编写处理逻辑
引用资料 :
  1. 禅道框架源码
  2. DeepSeek


发表评论
博客分类