欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  后端开发

[Modern PHP] 第二章 新特性之三 Traits

程序员文章站 2022-05-09 13:24:02
...
Traits

我的许多PHP开发者朋友都不太了解traits,这是PHP 5.4.0中引入的新概念。traits看起来像接口但是用起来像类,那究竟是什么呢?两者都不是。

一个trait拥有部分实现(譬如常量、属性和方法),可以被植入到一个或者多个实际的PHP类中。trait有两个职责:表明一个类可以做什么(类似接口);提供一个模块化的实现(类似类)。

在其他的语言中你也许已经对traits有了一定的了解。譬如Ruby的modules及mixins功能就和PHP的traits很类似。

我们为什么要使用traits

PHP语言使用的是经典的继承模型。这就意味着你从一个提供了基本实现的通用的根类开始。从根类创造出更具体的类,它们直接继承了父类的各种实现。这叫做继承层次,许多编程语言使用的都是这种公用的模式。

为了便于理解,假设你穿越回高中学习生物。还记得你学习的生物的界门纲目科属种吗?总共有六大界,界派生出门、门派生出纲、纲派生出目、目派生出科、科派生出属、属后面是种。在物种的层级上的每次向下的派生都代表着具体的特性。

经典的继承模型大多数情况下都能工作的很好。但是,如果有两个毫无关联的类需要实现类似的行为该如何解决?例如,一个PHP类叫RetailStore,而另一个PHP类叫Car,它们俩可以是说是完全风马牛不相及的两个类,在继承关系上根本无法共享一个公用的父类。然而两个类都需要使用地理位置中的经度和纬度来显示地图坐标。

我们创造traits就是来解决这个问题的。它们可以将部分实现注入到不相干的类中。traits也同样利于代码的重用。

遇到这个问题,我的第一个解决方案(也是最糟糕的)是创造一个公共的父类Geocodable用来给RetailStore和Car这两个类来继承。这个解决方案实在是太糟糕了,因为强行让两个毫不相干的类取共享一个公共的祖先,在它们各自的继承层级上都显得格外别扭。

我的第二个解决方案(稍微好点)是创造一个Geocodable接口来定义实现地理位置需要哪些方法。RetialStore和Car两个类都可以实现这个Geocodable接口。让每个类都能保留各自自然的继承关系确实是个很好的解决方案。但是我们还是需要在每个类里都重复的去实现接口里的定义,这可不是一个DRY的方案。

DRY是Do not repeat yourself的缩写。作为一个好的编程习惯,我们永远不要在多个地方重复同样的代码。不能出现因为改了一处代码,而被动的还要去修改其他地方同样的代码的情况。

我的第三个方案(最佳方案)是构造一个Geocodable的trait,在里面定义并实现相关的方法。我可以在不打乱类的继承层级的情况下把Geocodable的trait添加到RetailStore类和Car类中。

如何构造一个trait

下面展示的是如何定义一个PHP的trait:


作为一个好习惯,我们应该做到一个文件一个trait,就像类和接口的定义一样。

让我们回到我们的Geocodable例子来更好的演示一下trait的使用。我们都知道RetailStore类和Car类需要支持地理位置的定位功能,而我们也都能认同继承和接口并不是最佳方案。取而代之,我们构造一个Geocodable trait来返回一个可以在地图上标记的经度和纬度坐标。例子2-12中是我们完整的Geocodable trait。

例子 2-12 Geocodable trait的定义

geocoder = $geocoder;
    }

    public function setAddress($address)
    {
        $this->address = $address;
    }

    public function getLatitude()
    {
        if (isset($this->geocoderResult) === false) {
            $this->geocodeAddress();
        }

        return $this->geocoderResult->getLatitude();
    }

    public function getLongitude()
    {
        if (isset($this->geocoderResult) === false) {
            $this->geocodeAddress();
        }

        return $this->geocoderResult->getLongitude();
    }

    protected function geocodeAddress()
    {
        $this->geocoderResult = $this->geocoder->geocode($this->address);

        return true;
    }
}

Geocodable trait仅仅只定义了实现地理位置功能所需的属性和方法而并没有额外的任何功能。

我们的Geocodable trait定义了三个类的属性:

未完待续……

以上就介绍了[Modern PHP] 第二章 新特性之三 Traits,包括了方面的内容,希望对PHP教程有兴趣的朋友有所帮助。