Home > Article > Backend Development > PHP Basics Tutorial 11: Encapsulation, Inheritance, and Polymorphism
Encapsulation
Inheritance
Polymorphism
Overloading
Overriding
PHP’s object-oriented approach is the same as JAVA’s object-oriented approach, and both are divided into three major features: encapsulation, inheritance, and polymorphism. These three features optimize object-oriented in many aspects. These three characteristics are also issues that need to be considered when developing object-oriented.
What is encapsulation in object-oriented?
Encapsulation: Encapsulate the abstracted data and operations on the data together. The data is protected internally. Other parts of the program can only operate on the data through authorized operations (member methods).
As mentioned above, abstraction is to extract the common attributes and behaviors (methods) of a class of things to form a template (class). This method of studying problems is called abstraction.
Just like our bank account, no matter whose account it is, it includes the account number and password. There are also some common methods for withdrawing money, depositing money, and checking the balance. Our idea of using encapsulation is:
<?php class Account{ public $account_no; private $pwd; private $balance; public function __construct($account_no, $pwd = '123456', $balance = 0.0){ $this->account_no = $account_no; $this->pwd = $pwd; $this->balance = $balance; } //存款 public function deposit($amount, $pwd){ if($pwd == $this->pwd){ echo '<br> 存款成功'; $this->balance += $amount; }else{ echo '<br> 密码不正确'; } } //取款 public function withdraw($amount, $pwd){ if($pwd == $this->pwd){ if($this->balance >= $amount){ echo '<br> 取款成功'; $this->balance -= $amount; }else{ echo '<br> 余额不足..'; } }else{ echo '<br>密码错误'; } } //查询 public function query($pwd){ if($pwd == $this->pwd){ echo '<br> 账号' . $this->account_no . ' 余额=' . $this->balance; }else{ echo '<br> 查询密码错误'; } } } $account = new Account(123456789, 'hsp123', 2000000); $account->deposit(30000, 'hsp123'); $account->query('hsp123'); $account->withdraw(99999900, 'hsp123'); $account->query('hsp123');
The above code is the encapsulation of banking business, through encapsulation code. When we operate, we only need to call the methods provided inside, without having to manage how the business inside is handled. This is an important thought. The way we implement encapsulation is to use access modifiers and use the access characteristics of access modifiers to encapsulate properties or methods that are not allowed to be used externally. In the previous section we talked about three access modifiers.
So how do we access protected and private member properties? There are three methods here.
The first one
Use the magic methods __get() and __set() to access
<?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 '没有这个属性'; } } public function __get($name){ return $this -> $name; } } $cat = new Cat('小白',2); $cat -> name = '小花'; echo $cat -> name; ......结果...... 小花
We use the magic methods set and get attribute to change the value of a protected property, but this method is not recommended. Because it is not flexible enough, it cannot verify the incoming data. The following method can verify the incoming data.
The second type
provides a pair of getXxx() and setXxx() methods for each private or protected member variable.
<?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('12345' == $password){ return $this -> name; }else{ echo '密码不对'; } } } $cat = new Cat('小白',2); $cat -> setName('小花'); echo $cat -> getName('12345'); ......结果...... 小花
Use this method to judge the incoming data. We recommend this method.
The third type
Use a unified method to operate on attributes.
<?php class Cat{ private $name; private $age; public function __construct($name,$age){ $this -> name = $name; $this -> age = $age; } //显示对象的信息 public function showInfo(){ echo $this -> name . '的年龄是:' . $this-> age; } } $cat = new Cat('小白',2); $cat -> showInfo(); ......结果...... 小白的年龄是:2
Of these three methods, we use the second and third methods more often in development. When operating on a single attribute, you can use the second method. When operating on multiple attributes When , you can use the third option.
There are too many cases where inheritance is used in development. The emergence of inheritance reduces the redundancy of code. The code looks clearer. So what is inheritance?
<?php //定义一个小学生,它有考试的方法,设置成绩的方法 class Pupil { public $name; public $age; private $grade; public function __construct($name, $age){ $this->name = $name; $this->age = $age; } public function showInfo(){ echo '<br> 学生的信息如下:'; echo '<br> 学生的名字是:' . $this->name; echo '<br> 学生的成绩是:' . $this->grade; } //设置成绩 public function setGrade($grade){ $this->grade = $grade; } public function testing(){ echo '<br> 小学生在考试.........'; } } //定义一个大学生,它有考试的方法,设置成绩的方法 class Graduate { public $name; public $age; private $grade; public function __construct($name, $age){ $this->name = $name; $this->age = $age; } public function showInfo(){ echo '<br> 学生的信息如下:'; echo '<br> 学生的名字是:' . $this->name; echo '<br> 学生的成绩是:' . $this->grade; } //设置成绩 public function setGrade($grade){ $this->grade = $grade; } public function testing(){ echo '<br> 大学生在考试.....'; } } //使用一下 $pupil1 = new Pupil('小明', 40); $pupil1->testing(); $pupil1->setGrade(100); $pupil1->showInfo(); echo '<br>'; //使用 $graduate1 = new Graduate('小华', 20); $graduate1->testing(); $graduate1->setGrade(60); $graduate1->showInfo();
In the above code we can see that both classes have the same properties and methods. If there are many such codes in our code, it will cause code redundancy, which is not conducive to Our maintenance. The best way to solve this problem is to use inheritance to improve code reusability.
Inheritance syntax:
class 方法名 extends 父类的方法名{ }
Inheritance uses the keyword extends to inherit. It can be understood as code reuse, which makes our programming closer to human thinking. When multiple classes have the same attributes (variables) and methods, we can extract the parent class from these classes and define these same properties in the parent class. Properties and methods, all subclasses do not need to redefine these properties and methods, they only need to inherit the parent class
<?php //使用继承来完成 class Student{ public $name; public $age; private $grade; public function __construct($name, $age){ $this->name = $name; $this->age = $age; } public function showInfo(){ echo '<br> 学生的信息如下:'; echo '<br> 学生的名字是:' . $this->name; echo '<br> 学生的成绩是:' . $this->grade; } //设置成绩 public function setGrade($grade){ $this->grade = $grade; } } //这里 extends 关键字就是表示 Pupil 类 继承了 Student类 class Pupil extends Student{ public function testing(){ echo '<br> 小学生在考试.........'; } } class Graduate extends Student{ public function testing(){ echo '<br> 大学生在考试.........'; } } $pupil1 = new Pupil('小明', 40); $pupil1->testing(); $pupil1->setGrade(100); $pupil1->showInfo(); echo '<br>'; $graduate1 = new Graduate('小华', 20); $graduate1->testing(); $graduate1->setGrade(60); $graduate1->showInfo();
You can see that the same properties and methods are exported to the Student class in the above code (Parent class), we write two more classes to inherit the parent class through the extends keyword.
The class used to inherit is called the parent class, and the class that inherits this class is called a child class.
As long as we inherit, we can use the properties and methods in the parent class. However, if the attributes or methods of the parent class are modified by private, the subclass cannot inherit . This is the difference between protected and private.
The subclass obtains the attributes and methods of the parent class through inheritance, so does it mean that a copy of the parent class's code is assigned to the subclass? Not really. Inheritance is not simply copying the attribute and method definitions of the parent class to the subclass, but establishing a search relationship. When we visit again, this search relationship is:
When an object operates properties and methods, first check whether there are corresponding properties and methods in this class. If If there is, it will be judged whether there is access permission. If it can be accessed, access it. If it is not accessible, an error will be reported.
When an object operates properties and methods, first check them in this class. Are there corresponding attributes and methods? If not, it will look for its own parent class. If the parent class has it, it will then determine whether it can be accessed. If it can be accessed, it will be accessed. If it is not, an error will be reported.
The search logic goes to the top-level class. . . . .
There are still many things that need attention in inheritance:
子类最多只能继承一个父类(指直接继承),这和c++是不一样的。
子类可以继承其父类(或者基类)的 public ,protected修饰的变量(属性) 和 函数(方法)
在创建某个子类对象时,默认情况下会自动调用其父类的构造函数(指在子类没有自定义构造函数情况时)
<?php class A{ public function __construct(){ echo '父类的构造函数'; } } class B extends A{ } //子类没有定义构造函数,就会执行父类的构造函数。 $b = new B(); .....结果...... 父类的构造函数
如果在子类中需要访问其父类的方法(构造方法/成员方法 方法的访问修饰符是public/protected),可以使用父类::方法名(或者 parent::方法名 ) 来完成
<?php class A{ public function __construct(){ echo '父类的构造函数<br>'; } } class B extends A{ //子类定义了自己的构造函数,不会调用父类的构造函数。 public function __construct(){ parent::__construct(); echo '子类的构造函数<br>'; } } //子类没有定义构造函数,就会执行父类的构造函数。 $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 '对象<br>'; } //参数是数组 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 '<br> 猫猫名字是' . $this->name; } } //狗类 class Dog extends Anmial{ public function showInfo(){ echo '<br> 狗名字是' . $this->name; } } //猴子类 class Monkey extends Anmial{ public function showInfo(){ echo '<br> 猴子名字是' . $this->name; } } //定义事物 class Food{ public $name; public function __construct($name){ $this->name = $name; } } class Fish extends Food{ public function showInfo(){ echo '<br> ' . $this->name; } } class Bone extends Food{ public function showInfo(){ echo '<br> ' . $this->name; } } class Peach extends Food{ public function showInfo(){ echo '<br>' . $this->name; } } //主人类 class Master{ public $name; public function __construct($name){ $this->name = $name; } //主人可以喂食 //当我们的类型约束是父类时,可以接受 他的子类的对象实例 public function feed(Anmial $aniaml, Food $food){ echo '<br> 主人 ' . $this->name; $aniaml->showInfo(); //使用多态,根据传入的值不同,调用不同的方法。 echo '<br> 喂得食物是'; $food->showInfo(); } } $dog = new Dog('黄狗'); $cat = new Cat('花猫'); $monkey = new Monkey('猴'); $fish = new Fish('鲨鱼'); $bone = new Bone('骨头'); $peach = new Peach('桃子'); $master = new Master('小明'); $master->feed($dog, $bone); echo '<br><br>'; $master->feed($cat, $fish); echo '<br><br>'; $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 == 'add'){ //得到数组里面的参数,确定几个参数 $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 '<br>'; echo $calculate -> add(1,2,3); .....结果...... 3 6
看到代码有没有被欺骗的感觉-_-,先把类中的两个方法设置成private,这样在类外就访问不到,当我们在类外访问add方法的时候,会调用魔术方法,然后通过传入的数组的个数,确定参数的个数,从而在魔术方法中去掉用合适的方法。这就是PHP的方法重载。
在PHP面向对象编程中,当你去给一个不存在的属性赋值时,PHP默认会’动态的’, 给你创建一个对应的属性,这个称为属性重载。
<?php class A{ //在类中只定义一个变量 public $name = '小明'; } $a = new A(); echo '<pre class="brush:php;toolbar:false">'; 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 '吃饭<br>'; } } class Cat extends Animal{ //对父类的方法进行重写 public function eat(){ echo '猫在吃饭<br>'; } } $cat = new Cat(); $cat -> eat(); .....结果...... 猫在吃饭
如果父类的方法中使用了类型约束,那么子类的类型约束也必须一样。
在方法的重写中:
子类的方法的参数个数 ,方法名称,要和父类方法的参数个数,方法名称一样
子类方法不能缩小父类方法的访问权限(可以大于可以等于)
注意:如果父类的方法名是private,则子类并不会进行重写。
对于属性的重写也是,public 和 protected 可以被重写,private 的属性不能被重写
<?php class Animal{ public $name = '小花'; protected $age = 12; private $sex = '雄'; } class Cat extends Animal{ public $name = '小白'; protected $age = 4; private $sex = '雄'; } $cat = new Cat(); echo '<pre class="brush:php;toolbar:false">'; 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)!