zoukankan      html  css  js  c++  java
  • modern php笔记---php (性状)

    modern php笔记---php (性状)

    一、总结

    一句话总结:

    trait是和继承一个层次的东西
    一个类use MyTrait;后,trait中的方法覆盖父类方法,当前类中的方法覆盖trait方法

    1、为什么使用性状?

    让两个无关的php类具有类似的行为

    2、命名空间导入和性状导入的区别?

    导入位置:命名空间和性状都使用use 关键字导入,可是导入的位置有所不同,命名空间、类、接口、函数 和常量在类的定义外导入,而性状在类的定义体内导入。这个区别虽然小,但很重要。

    3、trait实现的原理?

    trait中的方法覆盖父类方法:从基类继承的成员会被trait插入的成员覆盖,优先顺序是来自当前类的成员覆盖了trait方法,而trait方法则覆盖了被继承的方法。

    4、trait实例?

    trait使用的话直接use MyTrait;即可
    一个类use MyTrait;后,trait中的方法覆盖父类方法,当前类中的方法覆盖trait方法
    <?php
    trait MyTrait
    {
        public function name()
        {
            echo 'trait';
        }
    }
    
    class Base
    {
        public function name()
        {
            echo 'base';
        }
    }
    
    class Sub extends Base
    {
        use MyTrait;
    }
    
    echo (new Sub())->name(); // trait 

    二、php (性状)

    转自或参考:php (性状)
    https://blog.csdn.net/qq_38287952/article/details/83316421

    什么性状?

    性状是类的部分实现(既常量,属性和方法),可以混入一个或多个现有的php类中。 * 性状有两个作用:表明类可以做什么(像是接口),提供模块化实现(像是类)。

    为什么使用性状?

    php语言使用一中典型的继承模型。在这种模型中,我们先编写一个通用的跟类,实现 基本的功能,然后扩展这个类,创建更具体的类,从直接父类继承实现。这叫做继承 层次结构,很多变成语言都使用了这个模式。 大多数的时候,这种典型的继承模型能良好运作,可是,如果想让两个无关的php类具有 类似的行为,应该什么做呢?例如,RetailStore和Car两个php类的作用十分不同,而 且在继承层次结构中没有共同的父类。不过,这两个类都应该能使用地理编码技术转换成经纬度,然后在地图上显示。 性状就是为了解决这个问题而诞生的。性状能把模块化的实现方式注入多个无关的类中。而且性状还能促进代码重用 为了解决这个问题,我的第一反应是创建一个父类Geocodable(这么做不好),让RetailStore 和Car都继承这个类。这种解决办法不好,因为我们强制让两个无关的类继承同一个祖先,而且很明显, 这个祖先不属于各自的继承层次结构。 我的第二反应是创建Gocodable接口(这么做更好),定义实现地理编码功能需要哪些方法,然后让RetailStore和Car类都实现这个接口。这种解决方法好,因为每个类都能保有自然的继承层次结构。不过,我们要在 两个类中重复实现相同的地理编码功能。我的第三个想法是创建Geocodable性状(这么做最好),定义并实现地理编码相关的方法,然后把在RetailStore 和Car类中混入这个性状。这么做不会扰乱这两个类原本自然的继承层次结构。

    如何创建性状 ?

    php 性状的定义方式如下:

    <?php
    trait MyTrait {
        // 这里是性状的实现
    }

    建议:建议与定义类和接口一样,一个文件只定义一个性状,这是良好的实践。

    我们回到Gecodable那个例子,通过实例更好地演示如何定义性状。我们希望Retail-Store和类 都提供地理编码功能,而且认识到继承和接口都不是最佳方案。我们选择的方案是创建Geocodable性状 返回经纬度。而且在地图中绘制。Gecodable性状的完整定义如下;

    trait Geocodable
    {
    
        /**
         * @var string
         */
        protected $address;
    
        /**
         * @var string
         */
        protected $gecoder;
    
        /**
         * @var string
         */
        protected $gecoderResult;
    
        public function setGeocoder(GeocodableGeocoderInterface $geocoder)
        {
            $this->gecoder = $geocoder;
        }
    
        public function setAddress($address)
        {
            $this->address = $address;
        }
    
        public function getLatitude()
        {
            if (isset($this->gecoderResult) === false) {
                $this->geocodeAddress();
            }
    
            return $this->gecoderResult->getLatitude();
        }
    
        public function getLongtude()
        {
            if (isset($this->gecoderResult) === false) {
                $this->geocodeAddress();
            }
    
            return $this->gecoderResult->getLatitude();
        }
    
        protected function geocodeAddress()
        {
            $this->gecoderResult = $this->gecoder->geocode($this->address);
    
            return true;
        }
    }
    

    如何使用性状?

    class MyClass
    {
        use MyTrait;
    
        // 这里是类的实现
    
    }

    命名空间和性状都使用use 关键字导入,可是导入的位置有所不同,命名空间、类、接口、函数 和常量在类的定义外导入,而性状在类的定义体内导入。这个区别虽然小,但很重要。

    本篇的例子出自Modern php书中。

    优先级

    从基类继承的成员会被trait插入的成员覆盖,优先顺序是来自当前类的成员覆盖了trait方法,

    而trait方法则覆盖了被继承的方法。

    <?php
    trait MyTrait
    {
        public function name()
        {
            echo 'trait';
        }
    }
    
    class Base
    {
        public function name()
        {
            echo 'base';
        }
    }
    
    class Sub extends Base
    {
        use MyTrait;
    }
    
    echo (new Sub())->name(); // trait 
    

    这里的trait覆盖了base的方法,说明了trait的优先级高

    <?php
    trait MyTrait
    {
        public function name()
        {
            echo 'trait';
        }
    }
    
    
    class Sub
    {
        use MyTrait;
        public function name()
        {
            echo 'sub';
        }
    }
    
    echo (new Sub())->name(); // sub

    这里的sub方法覆盖了 trait 方法,说明了 sub类的优先级高于性状。

    多个 trait

    <?php
    trait MyTrait
    {
        public function trait1()
        {
            echo 'MyTrait1, ';
        }
    }
    
    trait MyTrait2
    {
        public function trait2()
        {
            echo 'MyTrait2, ';
        }
    }
    
    
    class Sub
    {
        use MyTrait, MyTrait2;
        public function name()
        {
            echo 'sub';
        }
    }
    
    echo (new Sub())->trait1();
    echo (new Sub())->trait2();
    echo (new Sub())->name();

    以上会输出 MyTrait1, MyTrait2, sub

    冲突的解决。

    如果两个 trait 都插入了一个同名的方法,如果没有明确解决冲突将会产生一个致命错误。

    为了解决多个 trait 在同一个类中的命名冲突,需要使用 insteadof 操作符来明确指定使用冲突方法中的哪一个。

    以上方式仅允许排除掉其它方法,as 操作符可以将其中一个冲突的方法以另一个名称来引入。

    <?php
    trait A {
        public function smallTask()
        {
            echo 'a';
        }
    
        public function bigTalk()
        {
            echo 'A';
        }
    }
    
    trait B {
        public function smallTask()
        {
            echo 'b';
        }
    
        public function bigTalk()
        {
            echo 'B';
        }
    }
    
    class Talker
    {
        use A, B {
            B::smallTask insteadof A;
            A::bigTalk insteadof B;
        }
    }
    
    class Aliased_Talker {
        use A, B {
            B::smallTask insteadof A;
            A::bigTalk insteadof B;
            B::bigTalk as talk;
        }
    }

    在本例中 Talker 使用了 trait A 和 B。由于 A 和 B 有冲突的方法,其定义了使用 trait B 中的 smallTalk 以及 trait A 中的 bigTalk。

    Aliased_Talker 使用了 as 操作符来定义了 talk 来作为 B 的 bigTalk 的别名。

    修改方法的访问控制

    <?php
    trait HelloWorld
    {
        public function sayHello()
        {
            echo 'hello World';
        }
    }
    
    class MyClass1 {
        use HelloWorld {sayHello as protected; }
    }
    
    class MyClass2 {
        use HelloWorld {sayHello as private myPrivateHello; }
    }

    从 trait 来组成 trait

    正如类能够使用 trait 一样,其它 trait 也能够使用 trait。在 trait 定义时通过使用一个或多个 trait,它能够组合其它 trait 中的部分或全部成员。

    从 trait 来组成 trait的例子

    <?php
    trait Hello
    {
        public function sayHello()
        {
            echo 'hello';
        }
    }
    
    trait World
    {
        public function sayWorld()
        {
            echo 'World';
        }
    }
    
    trait HelloWorld
    {
        use Hello, World;
    }
    
    class MyHelloWorld
    {
        use HelloWorld;
    }
    
    $obj = new MyHelloWorld();
    $obj->sayHello();
    $obj->sayWorld();

    以上例子输出: helloWorld

    Trait 的抽象成员

    为了对使用的类施加强制要求,trait 支持抽象方法的使用。

    表示通过抽象方法来进行强制要求的例子

    <?php
    trait Hello
    {
        public function sayHelloWorld()
        {
            echo 'hello' . $this->getWorld();
        }
        abstract public function getWorld();
    }
    
    class MyHelloWorld
    {
        private $world;
        use Hello;
        public function getWorld()
        {
            return $this->world;
        }
    
        public function setWorld($val)
        {
            $this->world = $val;
        }
    }

    Trait 的静态成员

    Traits 可以被静态成员静态方法定义。

    静态变量的例子

    <?php
    trait Counter
    {
        public function inc()
        {
            static $c = 0;
            $c = $c + 1;
            echo "$c
    ";
        }
    }
    
    class C1
    {
        use Counter;
    }
    
    class C2
    {
        use Counter;
    }
    
    (new C1())->inc();
    (new C2())->inc();

    静态方法的例子

    <?php
    
    trait StaticExample
    {
        public static function doSomething()
        {
            return 'Doing something';
        }
    }
    
    class Example
    {
        use StaticExample;
    }
    
    Example::doSomething();

    静态变量和静态方法的例子

    <?php
    trait Counter {
        public static $c = 0;
        public static function inc() {
            self::$c = self::$c + 1;
            echo self::$c . "
    ";
        }
    }
    
    class C1 {
        use Counter;
    }
    
    class C2 {
        use Counter;
    }
    
    C1::inc(); // echo 1
    C2::inc(); // echo 1
    ?>

    属性

    Trait 同样可以定义属性。

    定义属性的例子

    <?php
    trait PropertiesTrait {
        public $x = 1;
    }
    
    class PropertiesExample {
        use PropertiesTrait;
    }
    
    $example = new PropertiesExample;
    $example->x;

    如果 trait 定义了一个属性,那类将不能定义同样名称的属性,否则会产生一个错误。如果该属性在类中的定义与在 trait 中的定义兼容(同样的可见性和初始值)则错误的级别是 E_STRICT,否则是一个致命错误。

    冲突的例子

    <?php
    trait PropertiesTrait {
        public $same = true;
        public $different = false;
    }
    
    class PropertiesExample {
        use PropertiesTrait;
        public $same = true; // Strict Standards
        public $different = true; // 致命错误
    }
    ?>

    Use的不同

    不同use的例子

    <?php
    namespace FooBar;
    use FooTest;  // means FooTest - the initial  is optional
    ?>
    
    <?php
    namespace FooBar;
    class SomeClass {
        use FooTest;   // means FooBarFooTest
    }
    ?>

    __CLASS__和__TRAIT__

    __CLASS__ 返回 use trait 的 class name,__TRAIT__返回 trait name

    <?php
    trait TestTrait {
        public function testMethod() {
            echo "Class: " . __CLASS__ . PHP_EOL;
            echo "Trait: " . __TRAIT__ . PHP_EOL;
        }
    }
    
    class BaseClass {
        use TestTrait;
    }
    
    class TestClass extends BaseClass {
    
    }
    
    $t = new TestClass();
    $t->testMethod();

    Trait单例

    实例如下

    <?php
    
    trait singleton {
        /**
         * private construct, generally defined by using class
         */
        //private function __construct() {}
    
        public static function getInstance() {
            static $_instance = NULL;
            $class = __CLASS__;
            return $_instance ?: $_instance = new $class;
        }
    
        public function __clone() {
            trigger_error('Cloning '.__CLASS__.' is not allowed.',E_USER_ERROR);
        }
    
        public function __wakeup() {
            trigger_error('Unserializing '.__CLASS__.' is not allowed.',E_USER_ERROR);
        }
    }
    
    /**
     * Example Usage
     */
    
    class foo {
        use singleton;
    
        private function __construct() {
            $this->name = 'foo';
        }
    }
    
    class bar {
        use singleton;
    
        private function __construct() {
            $this->name = 'bar';
        }
    }
    
    $foo = foo::getInstance();
    echo $foo->name;
    
    $bar = bar::getInstance();
    echo $bar->name;

    调用trait方法

    虽然不很明显,但是如果Trait的方法可以被定义为在普通类的静态方法,就可以被调用

    实例如下

    <?php 
    trait Foo { 
        public static function bar() { 
            return 'baz'; 
        } 
    } 
    
    echo Foo::bar(),"\n"; 
    ?>
     
  • 相关阅读:
    php -- php数组相关函数
    php -- 数组排序
    php -- in_array函数
    php -- 魔术方法 之 删除属性:__unset()
    无符号整型与有符号整型相运算规则
    N个节点的二叉树有多少种形态
    getopt_long
    typedef
    约瑟夫环问题算法(M)
    C语言基础
  • 原文地址:https://www.cnblogs.com/Renyi-Fan/p/11315752.html
Copyright © 2011-2022 走看看