• 2017.8.08


    1、抽象类

    在OOP语言中,一个类可以有一个或多个子类,而每个类都有至少一个公有方法做为外部代码访问其的接口。而抽象方法就是为了方便继承而引入的,我们先来看一下抽象类和抽象方法的定义再说明它的用途。

    什么是抽象方法?我们在类里面定义的没有方法体的方法就是抽象方法,所谓的没有方法体指的是,在方法声明的时候没有大括号以及其中的内容,而是直接在声明时在方法名后加上分号结束,另外在声明抽象方法时还要加一个关键字“abstract”来修饰;

    例如:

    abstract function fun1();  
    abstract function fun2();  

    上例是就是“abstract”修饰的没有方法体的抽象方法“fun1()”和“fun2()”,不要忘记抽象方法后面还要有一个分号;那么什么是抽象类呢?只要一个类里面有一个方法是抽象方法,那么这个类就要定义为抽象类抽象类也要使用“abstract”关键字来修饰;在抽象类里面可以有不是抽象的方法和成员属性。

    例如:

    abstract class Demo{  
        var $test;  
        abstract function fun1();  
        abstract function fun2();  
    function fun3(); }

    上例中定义了一个抽象类“Demo”使用了”abstract”来修饰, 在这个类里面定义了一个成员属性“$test”,和两个抽象方法“fun1”和“fun2”,还有一个非抽象的方法fun3();那么抽象类我们怎么使用呢?最重要的一点就是抽象类不能产生实例对象, 所以也不能直接使用,前面我们多次提到过类不能直接使用,我们使用的是通过类实例化出来的对象,那么抽象类不能产生实例对象我们声明抽象类有什么用呢?我们是将抽象方法是做为子类重载的模板使用的,定义抽象类就相当于定义了一种规范,这种规范要求子类去遵守,子类继承抽象类之后,把抽象类里面的抽象方法按 照子类的需要实现。子类必须把父类中的抽象方法全部都实现,否则子类中还存在抽象方法,那么子类还是抽象类,还是不能实例化类;为什么我们非要从抽象类中继承呢?因为有的时候我们要实现一些功能就必须从抽象类中继承,否则这些功能你就实现不了,如果继承了抽象类,就要实现类其中的抽象方法;

    class Test extends Demo{  
        function fun1(){  
            echo "Hi! Lilei"; //在抽象类中不能定义fun1的功能,在子类中可自行实现所需功能
        }  
      
        function fun2(){  
            echo "EN";  //在子类中需要实现抽象类中所规定的的所有事项
        }  
    }  
      
    $test=new Test();  
    $test->fun1();  
    $test->fun2(); 

    2、接口技术

    抽象类中的所有方法都是抽象方法时,我们就可以换另一种声明方式“接口”。接口中声明的方法必须都是抽象方法,只能使用const关键字声明为常量的成员属性,使用interface关键字标识。

    其实他们的作用很简单,当有很多人一起开发一个项目时,可能都会去调用别人写的一些类,那你就会问,我怎么知道他的某个功能的实现方法是怎么命名的呢,这个时候PHP接口类interface就起到作用了,当我们定义了一个接口类时,它里面的方式是下面的子类必须实现的,比如 :
     

    interface Shop  
    {  
    public function buy($gid);  
    public function sell($gid);  
    public function view($gid);  
    }  

    我声明一个shop接口类,定义了三个方法:买(buy),卖(sell),看(view),那么继承此类的所有子类都必须实现这3个方法少一个都不行,如果子类没有实现这些话,就无法运行。实际上接口类说白了,就是一个类的模板,一个类的规定,如果你属于这类,你就必须遵循我的规定,少一个都不行,但是具体你怎么去做,我不管,那是你的事,如:

    class BaseShop implements Shop  //实现接口用implements关键字
    {  
    public function buy($gid)  
    {  
    echo('你购买了ID为 :'.$gid.'的商品');  
    }  
    public function sell($gid)  
    {  
    echo('你卖了ID为 :'.$gid.'的商品');  
    }  
    public function view($gid)  
    {  
    echo('你查看了ID为 :'.$gid.'的商品');  
    }  
    }  

    结论 : PHP接口类interface就是一个类的领导者,指明方向,子类必须完成它指定方法。

    <?php
    
    interface Shop  
    {  
        public function buy($gid);  
        public function sell($gid);  
        public function view($gid);  
    }  
    
    class BaseShop implements Shop  //PHP是单继承的,只能有一个父类,但是可以有多个接口,多个接口之间用逗号隔开,但是在子类中需要将所有接口中的方法全部实现才可创建对象
    {          
        public function buy($gid)  
        {  
        echo('你购买了ID为 :'.$gid.'的商品');  
        }  
        public function sell($gid)  
        {  
        echo('你卖了ID为 :'.$gid.'的商品');  
        }  
        public function view($gid)  
        {  
        echo('你查看了ID为 :'.$gid.'的商品');  
        }  
    }  
    
    $haha = new BaseShop();
    $haha->buy('123');

    3、多态性

    多态面向对象的三大特性中除封装继承之外的另一重要特性。它展现了动态绑定的功能,也称为“同名异式”。多态的功能可让软件在开发和维护时,达到充分的延伸性。事实上,多态最直接的定义是让具有继承关系的不同类对象,可以对相同名称的成员函数调用,产生不同反应效果。所谓多态性就是指一段程序能够处理多种类型对象的能力,在PHP中,多态值指的就是方法的重写。方法重写是指一个子类中可以重新修改父类中的某些方法,使其具有自己的特征。重写要求子类的方法和父类的方法名称相同,这可以通过声明抽象类或是接口来规范。
    我们通过计算机USB设备的应用来介绍一下面向对象中的多态性,目前USB设置的种类仅我们自己用过的我想就有十几种吧。例如USB鼠标、USB键盘、USB存储设备等,这些计算机的外部设备都是通过USB接口连接到计算机以后,被计算机调用并启动运行的。也就是计算机正常运行的同时,没插入一种不同USB设备,就为计算机扩展一样功能,这正是我们所说的多态特征。那么为什么每个USB设备不一样,但都可以被计算机应用呢?那是因为每个USB设置都要遵守电脑USB接口的开发规范,都具有相同的能被计算机加载到并启用的方法,但运行各自相应的功能。这也正是我们对多态的定义,假设我们有一个主程序已经开发完成,需要后期由其他开发人员为其扩展一些功能,但需要在不改动主程序的基础上就可以加载到这些扩展的功能模块,其实也就是为程序开发一些插件。这就需要在主程序中需要为扩展的插件程序写好接口规范,然后每个插件只有按照规范去实现自己的功能,才能被主程序应用到。在计算机中应用USB设备的程序设计如下所示:

    <?php
    //定义一个USB接口,让每个USB设备都遵守这个规范
    interface USB{
    function run();//在接口下定义的方法必须而且只能是抽象类的,可省略abstruct关键字
    }
     
    class Computer{
    //计算机类中的一个方法可以应用任何一种USB设备
    function useUSB($usb){
    $usb->run();
    }
    }
     
    $computer =new Computer;
    $computer ->useUSB(new Ukey()); //为计算机插入一个usb键盘设备,并运行
    $computer ->useUSB(new Umouse()); //为计算机插入一个usb鼠标设备,并运行
    $computer ->useUSB(new Ustore()); //为计算机插入一个usb存储设备,并运行
    ?>

    下面的代码根据USB接口定义的规范,实现了USB键盘、USB鼠标和USB存储三个设备,当然可以去实现更多的USB设置,都按照自己设备的功能重写了run()方法,所以插入计算机启动运行后每个USB设备都有自己的形态。代码如下所示:

    <?php
    //扩展一个USB键盘设备,实现usb接口
    // class Ukey implements USB { function run(){ echo "运行USB键盘设备<br>"; } } //扩展一个USB鼠标设备,实现usb接口 class Umouse implements USB { function run(){ echo "运行USB鼠标设备<br>"; } } //扩展一个USB存储设备,实现usb接口 class Ustore implements USB { function run(){ echo "运行USB存储设备<br>"; } } ?>

    4、trait

    Trait是在PHP5.4中加入的,它既不是接口也不是类。主要是为了解决单继承语言的限制。是PHP多重继承的一种解决方案。例如,需要同时继承两个 Abstract Class, 这将会是件很麻烦的事情,Trait 就是为了解决这个问题。它能被加入到一个或多个已经存在的类中。它声明了类能做什么(表明了其接口特性),同时也包含了具体实现(表明了其类特性)

    简单使用

    首先,当然是声明个 Trait,PHP5.4 增加了 trait 关键字

    trait first_trait {
    function first_method() { /* Code Here */ }
    function second_method() { /* Code Here */ }
    }

    同时,如果要在 Class 中使用该 Trait,那么使用 use 关键字

    class first_class {
    // 注意这行,声明使用 first_trait
    use first_trait;
    }
    $obj = new first_class();
    // Executing the method from trait
    $obj->first_method(); // valid
    $obj->second_method(); // valid

    使用多个 Trait

    在同一个 Class 中可以使用多个 Trait

    trait first_trait
    {
    function first_method() { echo "method"; }
    }
    trait second_trait {
    function second_method() { echo "method"; } }
    class first_class { // now using more than one trait use first_trait, second_trait;//同时使用多个trait }
    $obj= new first_class(); // Valid $obj->first_method(); // Print : method // Valid $obj->second_method(); // Print : method

    Trait 之间的嵌套

    同时,Trait 之间也可以相互的嵌套,例如

    trait first_trait {
    function first_method() { echo "method"; }
    }
    trait second_trait {
    use first_trait;//在trait中使用use,将first_trait混入,形成嵌套 function second_method() { echo "method"; } }
    class first_class { // now using use second_trait; } $obj= new first_class(); // Valid $obj->first_method(); // Print : method // Valid $obj->second_method(); // Print : method

    Trait 的抽象方法(Abstract Method)

    我们可以在 Trait 中声明需要实现的抽象方法,这样能使使用它的 Class 必须实现它:

    trait first_trait {
    function first_method() { echo "method"; }
    // 这里可以加入修饰符,说明调用类必须实现它
    abstract public function second_method();//使用abstract修饰,说明调用类必须实现他
    }
    class first_method {
    use first_trait;
    function second_method() {
    /* Code Here */
    }
    }

    5、命名空间

    创建一个命名空间需要使用namespace关键字:命名空间一个最明确的目的就是解决重名问题,PHP中不允许两个函数或者类出现相同的名字,否则会产生一个致命的错误。这种情况下只要避免命名重复就可以解决,最常见的一种做法是约定一个前缀。但是加太多的前缀会使名字变得很长,那意味着以后使用Comment的时候会写上更多的代码(至少字符多了)。并且,以后如果要对各个模块增加更多的一些整合功能,或者是互相调用,发生重名的时候就需要重构名字。当然在项目开始的时候就注意到这个问题,并规定命名规则就能很好的避免这个问题。另一个解决方法可以考虑使用命名空间。

    注明:

    本文提到的常量:PHP5.3开始const关键字可以用在类的外部。const和define都是用来声明常量的(它们的区别不详述),但是在命名空间里,define的作用是全局的,而const则作用于当前空间。我在文中提到的常量是指使用const声明的常量。

    基础
    命名空间将代码划分出不同的空间(区域),每个空间的常量、函数、类(为了偷懒,我下边都将它们称为元素)的名字互不影响, 这个有点类似我们常常提到的‘封装'的概念。

    复制代码代码如下:
    <?php
    //创建一个名为'Article'的命名空间
    namespace Article;
    
    ?>

    要注意的是,当前脚本文件的第一个命名空间前面不能有任何代码,

    在同一脚本文件中可以创建多个命名空间。但是建议在每个独立文件内只定义一个命名空间,这样代码可读性最好。命名空间下面我创建了两个命名空间,顺便为这两个空间各自添加了一个Comment类元素:

    复制代码代码如下:
    <?php
    //创建一个名为'Article'的命名空间
    namespace Article;
    
    //此Comment属于Article空间的元素
    class Comment { }
    
    
    //创建一个名为'MessageBoard'的命名空间
    namespace MessageBoard;
    
    //此Comment属于MessageBoard空间的元素
    class Comment { }
    ?>

    在不同空间之间不可以直接调用其它元素,需要使用命名空间的语法:

    复制代码代码如下:
    <?php
    namespace Article;
    class Comment { }
    
    namespace MessageBoard;
    class Comment { }
    
    //调用当前空间(MessageBoard)的Comment类
    $comment = new Comment();
    
    //调用Article空间的Comment类
    $article_comment = new ArticleComment();
    
    ?>

    可以看到,在MessageBoard空间中调用article空间里的Comment类时,使用了一种像文件路径的语法: 空间名元素名

    除了类之外,对函数和常量的用法是一样的,下面我为两个空间创建了新的元素,并在MessageBoard空间中输出了它们的值。

    复制代码代码如下:
    <?php
     
    namespace Article;
    const PATH = '/article';
    function getCommentTotal() {
        return 100;
    }
    
    class Comment { }
    namespace MessageBoard;
    const PATH = '/message_board';
    function getCommentTotal() {
        return 300;
    }
    
    class Comment { }
    
    //调用当前空间的常量、函数和类
    echo PATH; ///message_board
    echo getCommentTotal(); //300
    $comment = new Comment();
    
    //调用Article空间的常量、函数和类
    echo ArticlePATH; ///article
    echo ArticlegetCommentTotal(); //100
    $article_comment = new ArticleComment();
    ?

    然后我的确得到了Article空间的元素数据。

    子空间:命名空间的调用语法像文件路径一样是有道理的,它允许我们自定义子空间来描述各个空间之间的关系。

    抱歉我忘了说,article和message board这两个模块其实都是处于同一个blog项目内。如果用命名空间来表达它们的关系,是这样:

    复制代码代码如下:
    <?php
     
    //我用这样的命名空间表示处于blog下的article模块
    namespace BlogArticle;
    class Comment { }
    
    //我用这样的命名空间表示处于blog下的message board模块
    namespace BlogMessageBoard;
    class Comment { }
    
    //调用当前空间的类
    $comment = new Comment();
    
    //调用BlogArticle空间的类
    $article_comment = new BlogArticleComment();
    
    ?>

    而且,子空间还可以定义很多层次,比如说 BlogArticleArchivesDate

    公共空间:没有定义命名空间的方法、类、库、常量都默认属于公共空间

    例如我有一个common_inc.php脚本文件,里面有一些好用的函数和类:

    复制代码代码如下:
    <?php
    function getIP() { }
    class FilterXSS { }
    ?>

    在一个命名空间里引入这个脚本,脚本里的元素不会归属到这个命名空间。如果这个脚本里没有定义其它命名空间,它的元素就始终处于公共空间中:

    复制代码代码如下:

    <?php
     
    namespace BlogArticle;
    //引入脚本文件
    include './common_inc.php';
    
    $filter_XSS = new FilterXSS(); //出现致命错误:找不到BlogArticleFilterXSS类
    $filter_XSS = new FilterXSS(); //正确
    
    ?>

    调用公共空间的方式是直接在元素名称前加 就可以了,否则PHP解析器会认为我想调用当前空间下的元素。除了自定义的元素,还包括PHP自带的元素,都属于公共空间。

    名称和术语:

    1.非限定名称,或不包含前缀的类名称,例如 $comment = new Comment();。如果当前命名空间是BlogArticle,Comment将被解析为BlogArticleComment。如果使用Comment的代码不包含在任何命名空间中的代码(全局空间中),则Comment会被解析为Comment。

    2.限定名称,或包含前缀的名称,例如 $comment = new ArticleComment();。如果当前的命名空间是Blog,则Comment会被解析为BlogArticleComment。如果使用Comment的代码不包含在任何命名空间中的代码(全局空间中),则Comment会被解析为Comment。

    3.完全限定名称,或包含了全局前缀操作符的名称,例如 $comment = new ArticleComment();。在这种情况下,Comment总是被解析为代码中的文字名(literal name)ArticleComment。
     

    其实可以把这三种名称类比为文件名(例如 comment.php)、相对路径名(例如 ./article/comment.php)、绝对路径名(例如 /blog/article/comment.php),这样可能会更容易理解。

    我用了几个示例来表示它们:

    <?php
     //创建空间Blog
    namespace Blog;
    class Comment { }
    //非限定名称,表示当前Blog空间
    //这个调用将被解析成 BlogComment();
    $blog_comment = new Comment();
    
    //限定名称,表示相对于Blog空间
    //这个调用将被解析成 BlogArticleComment();
    $article_comment = new ArticleComment(); //类前面没有反斜杆
    
    //完全限定名称,表示绝对于Blog空间
    //这个调用将被解析成 BlogComment();
    $article_comment = new BlogComment(); //类前面有反斜杆
    
    //完全限定名称,表示绝对于Blog空间
    //这个调用将被解析成 BlogArticleComment();
    $article_comment = new BlogArticleComment(); //类前面有反斜杆
    
    //创建Blog的子空间Article
    namespace BlogArticle;
    class Comment { }
    
    ?>

    别名和导入:别名和导入可以看作是调用命名空间元素的一种快捷方式。PHP并不支持导入函数或常量。

    它们都是通过使用use操作符来实现的

    <?php
    namespace BlogArticle;
    class Comment { }
    
    //创建一个BBS空间(我有打算开个论坛)
    namespace BBS;
    //导入一个命名空间
    use BlogArticle;
    //导入命名空间后可使用限定名称调用元素
    $article_comment = new ArticleComment();
    //为命名空间使用别名
    use BlogArticle as Arte;
    //使用别名代替空间名
    $article_comment = new ArteComment();
    //导入一个类
    use BlogArticleComment;
    //导入类后可使用非限定名称调用元素
    $article_comment = new Comment();
    //为类使用别名
    use BlogArticleComment as Comt;
    //使用别名代替空间名
    $article_comment = new Comt();
    
    ?>

     

  • 相关阅读:
    opencv imdecode和imencode用法
    caffe网络中屏蔽某一层的输出Silence层
    Opencv画图操作
    linux读取Windows的txt文件问题
    yolo检测系列
    c提取文件路径、文件名和后缀名
    draknet网络配置参数
    darknet源码解析
    Web_Toy
    Heart thing
  • 原文地址:https://www.cnblogs.com/yang01/p/7300699.html
Copyright © 2020-2023  润新知