面向对象之trait

面向对象之trait

场景

一个web站点,它有很多不同的类:用户(User)、页面(Page)、联系表单(ContactFrom)等。我们可能需要在每个类中添加一个方法的定义,但是这样的话就会造成不必要的代码冗余,并且一旦对该方法的定义有所修改,就需要改一大堆东西。

那我们能不能里用继承来实现上面问题呢;因为User类、Page类、ContactFrom类分别继承不同类,那要在不同类添加这个方法。依旧很惆怅。

一、为什么要用trait

在PHP中,每个类只能从单一的一个父类继承,这样的话就不能为多个类指定同一个比较通用的父类了,解决这个问题,那就是traits。

//最好在命名的时候以小写“t”开始。
trait tTools
{
    public function p($var)
    {
        echo '<pre>';
        var_dump($var);
        echo '</pre>';
    }
}
## cls
class Demo {
    use tTools;// 我们可以通过使用use关键字在一个类的定义中为这个类增加一个traits。
    function func() {
        return '一房';
    }
}

$obj = new Demo();
$abc = 12.34;
echo $obj->func();

$obj->p($abc);

二、使用trait

1、一般情况下,在trait中我们不加成员属性,只加成员方法

2、trait不能实例化

3、trait中的方法想让子类来使用,该方法必须是public

4、trait可以嵌套trait

三、优先级

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

class Base {
    public function sayHello() {
        echo 'Hello ';
    }
}

trait SayWorld {
    public function sayHello() {
        parent::sayHello();
        echo 'World!';
    }
}

class MyHelloWorld extends Base {
    use SayWorld;
}

$o = new MyHelloWorld();
$o->sayHello(); //Hello World!

另一个优先级顺序的例子

trait HelloWorld {
    public function sayHello() {
        echo 'Hello World!';
    }
}

class TheWorldIsNotEnough {
    use HelloWorld;
    public function sayHello() {
        echo 'Hello jack!';
    }
}

$o = new TheWorldIsNotEnough();
$o->sayHello();

四、多个trait情况

trait Hello {
    public function sayHello() {
        echo 'Hello ';
    }
}

trait World {
    public function sayWorld() {
        echo 'World';
    }
}

class MyHelloWorld {
    use Hello, World;
    public function sayExclamationMark() {
        echo '!';
    }
}

$o = new MyHelloWorld();
$o->sayHello();
$o->sayWorld();
$o->sayExclamationMark();

四、冲突的解决

如果两个trait中定义了一个同名的方法,那么在使用的时候就会报错。为了解决冲突,需要使用insteadof操作符来明确指定使用哪一个方法。另外还可以操作符可以将其中一个冲突的方法以另一个名称来引入。

trait ShenZhen 
{
    public function sayHello() {
        echo 'Hello ';
    }

    public function meizi(){
        echo '如花';
    }
}

trait LaoJia 
{
    public function sayWorld() {
        echo 'World';
    }

    public function meizi(){
        echo '铁锤妹妹';
    }
}

class You {
    use ShenZhen, LaoJia;

}

$o = new You();

1、覆盖-解决冲突

class You {
    use ShenZhen, LaoJia{
        ShenZhen::meizi insteadof LaoJia;
    }

}

2、别名-解决冲突

class You {
    use ShenZhen, LaoJia{
        ShenZhen::meizi insteadof LaoJia;
        LaoJia::meizi insteadof ShenZhen;//老家覆盖深圳的
        ShenZhen::meizi as qianren;//深圳的别名
    }

}

$o = new You();
$o->qianren();

3、修改方法的访问控制

class You {
    use ShenZhen{
        sayHello as protected;
    }
}

$o = new You();
$o->sayHello();

修改方法名

class My {
    use ShenZhen{
        sayHello as private talk;
    }

    function test(){
        $this->talk();
    }
}

$o = new My();
$o->test();

4、trait组成trait

trait Hello {
    public function sayHello() {
        echo 'Hello ';
    }
}

trait World {
    public function sayWorld() {
        echo 'World!';
    }
}

trait HelloWorld {
    use Hello, World;
}

class MyHelloWorld {
    use HelloWorld;
}

$o = new MyHelloWorld();
$o->sayHello();
$o->sayWorld();

5、Trait 的抽象

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;
    }
}

6、trait静态属性

1、静态变量

trait Counter {
    public function inc() {
        static $c = 0;
        $c++;
        echo "$c\n";
    }
}

class C1 {
    use Counter;
}

$o = new C1(); 
$o->inc(); // echo 1
$o->inc(); // echo 2
$o->inc(); // echo 3

2、静态方法

trait StaticExample {
    public static function doSomething() {
        return 'Doing something';
    }
}

class Example {
    use StaticExample;
}

Example::doSomething();

3、静态变量和静态方法

trait Counter {
    public static $c = 0;
    public function inc() {
       echo  self::$c++;
    }
}

class C1 {
    use Counter;
}

$o = new C1(); 
$o->inc(); // echo 0
$o->inc(); // echo 1
$o->inc(); // echo 2

7、trait定义属性

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

trait PropertiesTrait {
    public $same = true;
    public $different = false;
}

class PropertiesExample {
    use PropertiesTrait;
    public $same = true; // Strict Standards
    public $different = true; // 致命错误
}

8、Use的不同

<?php
namespace Foo\Bar;
use Foo\Test;  // means \Foo\Test
?>

<?php
namespace Foo\Bar;
class SomeClass {
    use Foo\Test;   // 用trait \Foo\Bar\Foo\Test
}

?>

9、 __CLASS____TRAIT__

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();

//Class: BaseClass
//Trait: TestTrait

10、trait的单例

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;




posted @ 2021-09-05 20:06  成文的博客  阅读(162)  评论(0编辑  收藏  举报