V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
lml12377
V2EX  ›  PHP

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

  •  
  •   lml12377 · 2016-07-02 15:55:10 +08:00 · 5122 次点击
    这是一个创建于 3060 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

    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 的场景)。

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

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

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