php 中 static 属性和方法的继承问题

2016-07-02 15:55:10 +08:00
 lml12377

网上关于静态属性和方法的继承问题,答案千奇百怪,干脆直接代码试了下:

class Base
{
    public static $var = 'var';

    public static function testStaticFun()
    {
        echo 'func';
    }
}

class A extends Base
{
    public function testSelf()
    {
        echo self::$var;
    }

    public function testParent()
    {
        echo parent::$var;
    }

    public function setSelf()
    {
        self::$var = 'self';
    }

    public function setParent()
    {
        parent::$var = 'parent';
    }

    public static function testStaticFun()
    {
        parent::testStaticFun();
        echo 'over';
    }
}

$objA = new A();     
  
$objA->testSelf();    // var
$objA->testParent();  // var

$objA->setSelf();
$objA->testSelf();    // self
$objA->testParent();  // self
echo Base::$var;      // self

$objA->setParent();
$objA->testSelf();    // parent
$objA->testParent();  // parent
echo Base::$var;      // parent

Base::testStaticFun();    // func
A::testStaticFun();       // func over

所以 php 中静态的继承究竟是什么情况?是不是和其他语言比如 java 在静态继承的行为上有区别?继承实现的原理是什么?

其实遇到这个问题主要是因为解决 model 引用数据库驱动的问题,因为一个业务逻辑下来 model 可能被 new 很多出来(不同的 model ),本来是在 model 基类里的 __construct 调用 DBFactory 工厂类的静态方法返回一个唯一的驱动对象的(比如: DBFactory::getDriverInstance('pdo'))。

但是感觉多此一举,于是想抛掉这个工厂类,直接让 model 基类来做这个事情,这时候就遇到上面的问题了,如果不用静态直接 model 基类的 __construct new 一个比如 Pdo 对象,虽然 UserModel/PostModel 全局只会有一个,但这两个模型对象都会持有一个 Pdo 对象,并且不是同一个。

于是想到 model 基类里放一个 static $dbInstance , model 的 __construct 负责给这个属性赋值,如果说继承类是用的同一个 $dbInstance ,那么我的目标就可以实现了?

还是说干脆写一个类似 thinkphp 的 M() 方法,只不过我叫 DB(),主要是为了获取数据库驱动对象的,方法里内置 static 变量,这样是 100% 可以的,并且还能通过接收参数临时切换驱动(业务里遇到过 mysql 切 mongo 的场景)。

但是既然也到静态继承的问题,就想搞个明白!请前辈指教!

5127 次点击
所在节点    PHP
14 条回复
jswh
2016-07-02 16:06:00 +08:00
PHP 的静态类型基本上可以理解为没有继承继承行为,静态属性该类的所有实例共用的。子类除非调用 parent 必须要用 parent ,否则修改就是自身的属性,不会影响父类的属性。如果父类方法中需要修改的是子类的静态属性,要用 static 关键字,而不要用 self , self 只会修改自身的属性,不会影响子类的属性。
jswh
2016-07-02 16:09:55 +08:00
你的方法是可行的,但是不建议这么做。
lml12377
2016-07-02 19:05:55 +08:00
@jswh 谢谢~ 那 static 参与继承这种场景一般在什么时候出现?是不是有什么特殊的用途还是说一般不这么用?那上面那个 model 的问题,是不是 factory 是比较正规的解决方法?类似 tp 的 M() 应该也算是类似吧?
young
2016-07-02 20:48:18 +08:00
imcxy
2016-07-02 21:59:07 +08:00
没见过静态成员参与继承。从生活习惯上也不符合习惯。继承的好处就是,可以通过派生类的一个实例的引用来访问基类成员。

而静态成员本来就可以用过类名直接访问。何须通过继承来多此一举。
jswh
2016-07-03 00:35:55 +08:00
@lml12377 我的话,静态成员一般是用来存储这个类中值固定,但又不是常量的值值。比如你的 data model 可能需要保存对应的表结构,这对所有的实例来说都是一样的,不想要每次都访问一遍数据库,就可以在第一个实例创建的时候把数据存储到静态成员上,后续的实例就可以共享这份数据。 我说不建议这么做,是因为 model 不应该知道 DB 的的实例是怎么来的,你只你有我要的接口就行了,否则就耦合在一起了。 Factory 是一种方式,但最好的还是注入进来。
lml12377
2016-07-03 12:12:43 +08:00
@imcxy 有道理!
lml12377
2016-07-03 12:13:45 +08:00
@young 那天还想着翻官方手册来着,没翻到。。。谢谢~
lml12377
2016-07-03 12:19:31 +08:00
@jswh 谢谢,答的很详细,消化一下~
lml12377
2016-07-03 13:47:49 +08:00
@jswh 我的 DB() 方法是不是一定程度上算得上是注入?比如 DB('pdo') 取代了我原先在 model 里的 new Pdo();
garrydzeng
2016-08-17 01:06:05 +08:00
@lml12377 不是, 你的代码仍然依赖了 DB 这个函数, 正确做法是从外部传进来, 比如通过构造器: __construct(PDO $db) 此时依赖的是 PDO 接口, 只是 PDO 没有相关的 interface 定义, 只好依赖这个类型...
lml12377
2016-08-17 09:20:03 +08:00
@garrydzeng 正好遇到这个问题,哥帮我看下: http://www.v2ex.com/t/299671
garrydzeng
2016-09-11 02:28:33 +08:00
@lml12377 最近才收到提醒, 一看已经过去二十几天... Phalcon 的控制反转看起来是 [Service Locator]( https://en.wikipedia.org/wiki/Service_locator_pattern) 模式, 不是依赖注入...
lml12377
2016-09-12 16:59:25 +08:00
@garrydzeng 这个,在研究了 spring 的 BeanFactory 之后基本有头绪了~谢谢

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/289811

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX