Heim >Backend-Entwicklung >PHP-Tutorial >PHP-Grundlagen-Tutorial 11: Kapselung, Vererbung und Polymorphismus

PHP-Grundlagen-Tutorial 11: Kapselung, Vererbung und Polymorphismus

黄舟
黄舟Original
2017-03-01 10:01:281711Durchsuche

Inhalt in diesem Abschnitt erklärt

  • Kapselung

  • Vererbung

  • Polymorph

  • Überladung

  • Überschreibend

Vorwort

Der objektorientierte Ansatz von PHP ist der gleiche wie der objektorientierte Ansatz von JAVA, und beide sind in drei Hauptmerkmale unterteilt: Kapselung, Vererbung und Polymorphismus. Diese drei Funktionen optimieren die Objektorientierung in vielerlei Hinsicht. Diese drei Merkmale sind auch Aspekte, die bei der objektorientierten Entwicklung berücksichtigt werden müssen.

Kapselung

Was ist Kapselung in objektorientierter Hinsicht?

Kapselung: Abstrahierte Daten und Operationen auf den Daten zusammen kapseln. Andere Teile des Programms können die Daten nur durch autorisierte Operationen (Mitgliedsmethoden) bearbeiten.

Oben wurde die Abstraktion erwähnt, bei der die gemeinsamen Attribute und Verhaltensweisen (Methoden) einer Klasse von Dingen extrahiert werden, um eine Vorlage (Klasse) zu bilden. Diese Methode zur Untersuchung von Problemen wird als Abstraktion bezeichnet.

Genau wie unser Bankkonto enthält es die Kontonummer und das Passwort, egal um wessen Konto es sich handelt, und es gibt auch einige gängige Methoden zum Abheben, Einzahlen und Überprüfen des Kontostands. Unsere Idee zur Verwendung der Kapselung ist:

<?php
    class Account{

        public $account_no;
        private $pwd;
        private $balance;

        public function __construct($account_no, $pwd = &#39;123456&#39;, $balance = 0.0){

            $this->account_no = $account_no;
            $this->pwd = $pwd;
            $this->balance = $balance;

        }

        //存款
        public function deposit($amount, $pwd){

            if($pwd == $this->pwd){
                echo &#39;<br> 存款成功&#39;;
                $this->balance += $amount;
            }else{
                echo &#39;<br> 密码不正确&#39;;
            }
        }

        //取款
        public function withdraw($amount, $pwd){
            if($pwd == $this->pwd){

                if($this->balance >= $amount){
                    echo &#39;<br> 取款成功&#39;;
                    $this->balance -= $amount;
                }else{
                    echo &#39;<br> 余额不足..&#39;;
                }
            }else{
                echo &#39;<br>密码错误&#39;;
            }
        }

        //查询
        public function query($pwd){

            if($pwd == $this->pwd){
                echo &#39;<br> 账号&#39; . $this->account_no . &#39; 余额=&#39; . $this->balance;
            }else{
                echo &#39;<br> 查询密码错误&#39;;
            }
        }

    }

    $account = new Account(123456789, &#39;hsp123&#39;, 2000000);

    $account->deposit(30000, &#39;hsp123&#39;);

    $account->query(&#39;hsp123&#39;);

    $account->withdraw(99999900, &#39;hsp123&#39;);

    $account->query(&#39;hsp123&#39;);

Der obige Code ist die Kapselung von Bankgeschäften durch Kapselungscode. Wenn wir arbeiten, müssen wir nur die darin bereitgestellten Methoden aufrufen, ohne uns darum kümmern zu müssen, wie das Geschäft im Inneren abgewickelt wird. Das ist ein wichtiger Gedanke. Die Art und Weise, wie wir die Kapselung implementieren, besteht darin, Zugriffsmodifikatoren zu verwenden und die Zugriffseigenschaften von Zugriffsmodifikatoren zu verwenden, um Eigenschaften oder Methoden zu kapseln, die nicht extern verwendet werden dürfen. Im vorherigen Abschnitt haben wir über drei Zugriffsmodifikatoren gesprochen.
Wie greifen wir also auf geschützte und private Mitgliedereigenschaften zu? Hier gibt es drei Methoden.

Die erste

verwendet die magischen Methoden __get() und __set(), um darauf zuzugreifen

<?php

    class Cat{
        private $name;
        private $age;
        public function __construct($name,$age){
            $this -> name = $name;
            $this -> age = $age;
        }

        public function __set($name,$value){
            //判断类中有没有这个属性
            if(property_exists($this,$name)){
                $this -> $name = $value;
            }else{
                echo &#39;没有这个属性&#39;;
            }
        }

        public function __get($name){
            return $this -> $name;
        }
    }

    $cat = new Cat(&#39;小白&#39;,2);
    $cat -> name = &#39;小花&#39;;
    echo $cat -> name;
    ......结果......
    小花

Wir verwenden die magischen Methoden set und get Attribut verwenden, um den Wert eines geschützten Attributs zu ändern. Diese Methode wird jedoch nicht empfohlen. Da es nicht flexibel genug ist, kann es die eingehenden Daten nicht überprüfen. Mit der folgenden Methode können die eingehenden Daten überprüft werden.

Der zweite Typ

stellt ein Paar getXxx()- und setXxx()-Methoden für jede private oder geschützte Mitgliedsvariable bereit.

<?php

    class Cat{
        private $name;
        private $age;
        public function __construct($name,$age){
            $this -> name = $name;
            $this -> age = $age;
        }

        public function setName($value){
            if(is_string($value)){
                $this -> name = $value;
            }
        }

        public function getName($password){
            if(&#39;12345&#39; == $password){
                return $this -> name;
            }else{
                echo &#39;密码不对&#39;;
            }
        }
    }

    $cat = new Cat(&#39;小白&#39;,2);
    $cat -> setName(&#39;小花&#39;);
    echo $cat -> getName(&#39;12345&#39;);
    ......结果......
    小花

Mit dieser Methode können die eingehenden Daten beurteilt werden. Wir empfehlen diese Methode.

Der dritte Typ

verwendet eine einheitliche Methode zur Bearbeitung von Attributen.

<?php

    class Cat{
        private $name;
        private $age;
        public function __construct($name,$age){
            $this -> name = $name;
            $this -> age = $age;
        }

        //显示对象的信息
        public function showInfo(){
            echo $this -> name . &#39;的年龄是:&#39; . $this-> age;
        }
    }

    $cat = new Cat(&#39;小白&#39;,2);
    $cat -> showInfo();
    ......结果......
    小白的年龄是:2

Von diesen drei Methoden verwenden wir die zweite und dritte Methode häufiger in der Entwicklung. Wenn Sie ein einzelnes Attribut bearbeiten, können Sie die zweite Methode verwenden Verwenden Sie die dritte Methode.

Vererbung

Es gibt zu viele Fälle, in denen Vererbung in der Entwicklung verwendet wird. Das Aufkommen der Vererbung verringert die Redundanz des Codes. Der Code sieht klarer aus. Was ist also Vererbung?

<?php
    //定义一个小学生,它有考试的方法,设置成绩的方法
    class Pupil {
        public $name;
        public $age;
        private $grade;

        public function __construct($name, $age){
            $this->name = $name;
            $this->age = $age;
        }

        public function showInfo(){
            echo &#39;<br> 学生的信息如下:&#39;;
            echo &#39;<br> 学生的名字是:&#39; . $this->name;
            echo &#39;<br> 学生的成绩是:&#39; . $this->grade;
        }
        //设置成绩
        public function setGrade($grade){
            $this->grade = $grade;
        }

        public function testing(){
            echo &#39;<br> 小学生在考试.........&#39;;
        }
    }

    //定义一个大学生,它有考试的方法,设置成绩的方法
    class Graduate {
        public $name;
        public $age;
        private $grade;

        public function __construct($name, $age){
            $this->name = $name;
            $this->age = $age;
        }

        public function showInfo(){

            echo &#39;<br> 学生的信息如下:&#39;;
            echo &#39;<br> 学生的名字是:&#39; . $this->name;
            echo &#39;<br> 学生的成绩是:&#39; . $this->grade;
        }
        //设置成绩
        public function setGrade($grade){
            $this->grade = $grade;
        }
        public function testing(){
            echo &#39;<br> 大学生在考试.....&#39;;
        }
    }

    //使用一下
    $pupil1 = new Pupil(&#39;小明&#39;, 40);
    $pupil1->testing();
    $pupil1->setGrade(100);
    $pupil1->showInfo();
    echo &#39;<br>&#39;;
    //使用
    $graduate1 = new Graduate(&#39;小华&#39;, 20);
    $graduate1->testing();
    $graduate1->setGrade(60);
    $graduate1->showInfo();

Im obigen Code können wir sehen, dass beide Klassen die gleichen Eigenschaften und Methoden haben. Wenn unser Code viele solcher Codes enthält, führt dies zu Coderedundanz und ist für unsere Wartung nicht förderlich. Der beste Weg, dieses Problem zu lösen, besteht darin, die Wiederverwendbarkeit des Codes durch Vererbung zu verbessern.

Geerbte Syntax:

class 方法名 extends 父类的方法名{

}

Geerbt mit dem Schlüsselwort extends. Es kann als Wiederverwendung von Code verstanden werden, wodurch unsere Programmierung dem menschlichen Denken näher kommt. Wenn mehrere Klassen dieselben Attribute (Variablen) und Methoden haben, können wir die übergeordnete Klasse aus diesen Klassen extrahieren und dieselben Eigenschaften in der übergeordneten Klasse definieren und Methoden, alle Unterklassen müssen diese Attribute und Methoden nicht neu definieren, sie müssen nur die übergeordnete Klasse erben

<?php

    //使用继承来完成
    class Student{

        public $name;
        public $age;
        private $grade;

        public function __construct($name, $age){
            $this->name = $name;
            $this->age = $age;
        }

        public function showInfo(){

            echo &#39;<br> 学生的信息如下:&#39;;
            echo &#39;<br> 学生的名字是:&#39; . $this->name;
            echo &#39;<br> 学生的成绩是:&#39; . $this->grade;
        }
        //设置成绩
        public function setGrade($grade){
            $this->grade = $grade;
        }

    }


    //这里 extends 关键字就是表示 Pupil 类 继承了 Student类
    class Pupil extends Student{

        public function testing(){
            echo &#39;<br> 小学生在考试.........&#39;;
        }
    }

    class Graduate extends Student{

        public function testing(){
            echo &#39;<br> 大学生在考试.........&#39;;
        }
    }

    $pupil1 = new Pupil(&#39;小明&#39;, 40);
    $pupil1->testing();
    $pupil1->setGrade(100);
    $pupil1->showInfo();
    echo &#39;<br>&#39;;
    $graduate1 = new Graduate(&#39;小华&#39;, 20);
    $graduate1->testing();
    $graduate1->setGrade(60);
    $graduate1->showInfo();

Sie können sehen, dass dieselben Attribute und Methoden in die Student-Klasse (übergeordnet) exportiert werden class) schreiben wir zwei weitere Klassen, um die übergeordnete Klasse über das Schlüsselwort „extends“ zu erben.
Die zum Erben verwendete Klasse wird als übergeordnete Klasse bezeichnet, und die Klasse, die diese Klasse erbt, wird als untergeordnete Klasse bezeichnet.
Solange wir erben, können wir die Eigenschaften und Methoden in der übergeordneten Klasse verwenden. Wenn jedoch das -Attribut oder die Methode der übergeordneten Klasse durch private geändert wird, kann die Unterklasse nicht erben. Dies ist der Unterschied zwischen geschützt und privat.

Die Unterklasse erhält die Attribute und Methoden der übergeordneten Klasse durch Vererbung. Bedeutet dies also, dass der Unterklasse eine Kopie des Codes der übergeordneten Klasse zugewiesen wird? Nicht wirklich. Bei der Vererbung werden nicht einfach die Attribut- und Methodendefinitionen der übergeordneten Klasse in die Unterklasse kopiert, sondern eine Suchbeziehung hergestellt. Bei einem erneuten Besuch lautet diese Suchbeziehung:

  1. Wenn ein Objekt Attribute und Methoden betreibt, prüfen Sie zunächst, ob in dieser Klasse entsprechende Attribute und Methoden vorhanden sind. Wenn ja, beurteilen Sie, ob dies der Fall ist ist die Zugriffsberechtigung. Wenn darauf zugegriffen werden kann, wird ein Fehler gemeldet.

  2. Wenn ein Objekt Eigenschaften und Methoden ausführt, sehen Sie sich diese zunächst an Gibt es entsprechende Attribute und Methoden? Wenn die übergeordnete Klasse darauf zugreifen kann, wird darauf zugegriffen Ist dies nicht der Fall, wird ein Fehler gemeldet.

  3. Diese Suchlogik geht zur obersten Klasse. . . . .

Beim Erbe gibt es noch viele Dinge zu beachten:

  • 子类最多只能继承一个父类(指直接继承),这和c++是不一样的。

  • 子类可以继承其父类(或者基类)的 public ,protected修饰的变量(属性) 和 函数(方法)

  • 在创建某个子类对象时,默认情况下会自动调用其父类的构造函数(指在子类没有自定义构造函数情况时)

    <?php
        class A{
            public function __construct(){
                echo &#39;父类的构造函数&#39;;
            }
        }
    
        class B extends A{
    
        }
        //子类没有定义构造函数,就会执行父类的构造函数。
        $b = new B();
        .....结果......
        父类的构造函数
  • 如果在子类中需要访问其父类的方法(构造方法/成员方法  方法的访问修饰符是public/protected),可以使用父类::方法名(或者 parent::方法名 ) 来完成

    <?php
        class A{
            public function __construct(){
                echo &#39;父类的构造函数<br>&#39;;
            }
        }
    
        class B extends A{
            //子类定义了自己的构造函数,不会调用父类的构造函数。
            public function __construct(){
                parent::__construct();
                echo &#39;子类的构造函数<br>&#39;;
            }
        }
        //子类没有定义构造函数,就会执行父类的构造函数。
        $b = new B();
        .....结果......
        父类的构造函数
        子类的构造函数
  • 如果子类中的方法和父类方法相同,我们称为方法重写。

多态

多态通俗的讲就是多种形态,就是指在面向对象中,对象在不同情况下的多种状态(根据使用的上下文)PHP天生就是多态语言,同时PHP可以根据传入的对象类型不同,调用对应对象的方法

在PHP中变量的定义不像其他语言在变量名前面定义类型,而是直接用$符号来定义,可以接受任意类型的值。这就为多态创造了条件。根据传入的对象类型不同,调用对应对象的方法,我们也可以使用类型约束来对传入的值进行约束。

类型约束

类型约束的基本概念是 PHP 5 可以使用类型约束。函数的参数可以指定必须为对象(在函数原型里面指定类的名字),接口,数组(PHP 5.1 起)或者 callable(PHP 5.4 起)。如前PHP只支持这几种。

<?php

    class  MyClass
    {
         //传进的参数约束为cat或者cat的子类       
        public function  test (Cat $cat ){
            echo &#39;对象<br>&#39;;
        }
        //参数是数组
        public function  test_array (array  $arr ) {
             print_r($arr);
        }
    }
    class  Cat  {
    }

    $cat = new Cat();
    $myClass = new MyClass();
    $arr = array(1,2,4,5);
    $myClass -> test($cat);
    $myClass -> test_array($arr);
    .....结果......
    对象
    Array ( [0] => 1 [1] => 2 [2] => 4 [3] => 5 )

如果要进行类型约束,在参数前面写上类型就行了,可以试一下传入的不是约束的类型,会报一个致命的错误。

多态的示例:

<?php
    //多态的案例
    //定义动物的父类
    class Anmial{
        public $name;
        public function __construct($name){
            $this->name = $name;
        }
    }
    //猫类
    class Cat extends Anmial{
        public function showInfo(){

            echo &#39;<br> 猫猫名字是&#39; . $this->name;
        }
    }
    //狗类
    class Dog extends Anmial{
        public function showInfo(){

            echo &#39;<br> 狗名字是&#39; . $this->name;
        }
    }
    //猴子类
    class Monkey extends Anmial{
        public function showInfo(){

            echo &#39;<br> 猴子名字是&#39; . $this->name;
        }
    }

    //定义事物
    class Food{
        public $name;
        public function __construct($name){
            $this->name = $name;
        }
    }

    class Fish extends Food{
        public function showInfo(){
            echo &#39;<br> &#39; . $this->name;
        }
    }

    class Bone extends Food{
        public function showInfo(){
            echo &#39;<br> &#39; . $this->name;
        }
    }

    class Peach extends Food{
        public function showInfo(){
            echo &#39;<br>&#39; . $this->name;
        }
    }


    //主人类
    class Master{
        public $name;
        public function __construct($name){
            $this->name = $name;
        }
        //主人可以喂食
        //当我们的类型约束是父类时,可以接受 他的子类的对象实例
        public function feed(Anmial $aniaml, Food $food){

            echo &#39;<br> 主人 &#39; . $this->name;
            $aniaml->showInfo(); //使用多态,根据传入的值不同,调用不同的方法。
            echo &#39;<br> 喂得食物是&#39;;
            $food->showInfo();

        }
    }

    $dog = new Dog(&#39;黄狗&#39;);
    $cat = new Cat(&#39;花猫&#39;);
    $monkey = new Monkey(&#39;猴&#39;);

    $fish = new Fish(&#39;鲨鱼&#39;);
    $bone = new Bone(&#39;骨头&#39;);

    $peach = new Peach(&#39;桃子&#39;);

    $master = new Master(&#39;小明&#39;);

    $master->feed($dog, $bone);
    echo &#39;<br><br>&#39;;
    $master->feed($cat, $fish);
    echo &#39;<br><br>&#39;;
    $master->feed($monkey, $peach);

上面的代码在主人的喂食方法中,使用类型约束,根据传进去的对象不同,调用不同对象的方法。

重载

方法的重载

重载:简单说,就是函数或者方法有相同的名称,但是参数列表不相同的情形,这样的同名不同参数的函数或者方法之间,互相称之为重载函数或者方法。

上面的关于方法的重载可以理解为在一个类中,有两个方法名一样的函数,但是函数的参数是不一样的,我们在调用的时候,系统会根据我们传入的参数的不同,而自动的调用不同的函数,这就是重载,但是在PHP中一个类中不能有两个方法名相同的函数,尽管你的参数不同,它会报一个Cannot redeclare的错误。那么在PHP中就不能重载了吗?其实可以的,利用魔术方法。

在上节中介绍魔术方法中有一个方法是当我们访问不可访问或不存在的方法时,系统自动调用的,也就是__call()方法。PHP中可以利用这个魔术方法进行重载

<?php
    class Calculate{

        //定义两个方法,计算加法,注意两个方法的方法名是不一样的
        private function add($a,$b,$c){ 
            return $a + $b + $c;
        }

        private function add1($a,$b){
            return $a + $b;
        }

        public function __call($name,$val_arr){
            if($name == &#39;add&#39;){
                //得到数组里面的参数,确定几个参数
                $num = count($val_arr);
                if($num == 2){
                    return $this -> add1($val_arr[0],$val_arr[1]);
                }else if($num == 3){
                    return $this -> add($val_arr[0],$val_arr[1],$val_arr[2]);
                }
            }
        }
    }

    $calculate = new Calculate();
    echo $calculate -> add(1,2);
    echo &#39;<br>&#39;;
    echo $calculate -> add(1,2,3);
    .....结果......
    3
    6

看到代码有没有被欺骗的感觉-_-,先把类中的两个方法设置成private,这样在类外就访问不到,当我们在类外访问add方法的时候,会调用魔术方法,然后通过传入的数组的个数,确定参数的个数,从而在魔术方法中去掉用合适的方法。这就是PHP的方法重载。

属性的重载

在PHP面向对象编程中,当你去给一个不存在的属性赋值时,PHP默认会’动态的’, 给你创建一个对应的属性,这个称为属性重载。

<?php

    class A{
        //在类中只定义一个变量
        public $name = &#39;小明&#39;;

    }
    $a = new A();
    echo &#39;<pre class="brush:php;toolbar:false">&#39;;
    var_dump($a);
    $a -> age = 12;
    var_dump($a);
    .....结果......
    object(A)#1 (1) {
      ["name"]=>
      string(6) "小明"
    }
    object(A)#1 (2) {
      ["name"]=>
      string(6) "小明"
      ["age"]=>
      int(12)
    }

从结果中可以看到,当我们给一个不存在属性age赋值后,在输出的类结构中出现了age这个属性,这就是属性的重载。

如果不想让类的属性自动增加,可以使用魔术方法__set()和__get()方法进行控制。

重写

方法的重写

在上面我们介绍了面向对象的继承机制。而重写是基于继承才引发出来的概念。当一个类继承了另外一个类后,在父类中有一个方法,子类继承,但是在子类中觉得父类的方法不能满足要求,需要子类在重写定义一个和父类的方法一样的方法进行重新的定义,称为重写。说白了就子类有一个方法,和父类(基类)的某个方法的名称、参数个数一样。

<?php

    class Animal{
        //动物都有吃这个行为,具体的要看是什么动物
        public function eat(){

            echo &#39;吃饭<br>&#39;;
        }
    }

    class Cat extends Animal{
        //对父类的方法进行重写
        public function eat(){
            echo &#39;猫在吃饭<br>&#39;;
        }       
    }
    $cat = new Cat();
    $cat -> eat();
    .....结果......
    猫在吃饭

如果父类的方法中使用了类型约束,那么子类的类型约束也必须一样。

在方法的重写中:

  1. 子类的方法的参数个数 ,方法名称,要和父类方法的参数个数,方法名称一样

  2. 子类方法不能缩小父类方法的访问权限(可以大于可以等于)

注意:如果父类的方法名是private,则子类并不会进行重写。

属性的重写

对于属性的重写也是,public 和 protected 可以被重写,private 的属性不能被重写

<?php

    class Animal{
        public $name = &#39;小花&#39;;
        protected $age = 12;
        private $sex = &#39;雄&#39;;
    }

    class Cat extends Animal{
        public $name = &#39;小白&#39;;
        protected $age = 4;
        private $sex = &#39;雄&#39;;
    }
    $cat = new Cat();
    echo &#39;<pre class="brush:php;toolbar:false">&#39;;
    var_dump($cat);
    ......结果......
    object(Cat)#1 (4) {
      ["name"]=>
      string(6) "小白"
      ["age":protected]=>
      int(4)
      ["sex":"Cat":private]=>
      string(3) "雄"
      ["sex":"Animal":private]=>
      string(3) "雄"
    }

可以看到name和age被重写了,private不能被重写。

总结

面向对象中,封装是一个很重要的思想,封装的实现,可以降低代码的耦合度,同时继承的时候减少代码的冗余度,php的独特语言结构让多态也散发着光芒,而重写的掌握,让我们对继承有了更深层次的了解。(ps:今天10.1号,祖国的生日,我要去给祖国母亲庆生去了)

 以上就是PHP基础教程十一之封装、继承、多态的内容,更多相关内容请关注PHP中文网(www.php.cn)!


Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn