Maison >développement back-end >tutoriel php >Tutoriel PHP Basics 11 : Encapsulation, héritage et polymorphisme
Encapsulation
Héritage
Polymorphique
Surcharge
Remplacement
L'approche orientée objet de PHP est la même que l'approche orientée objet de JAVA, et toutes deux sont divisées en trois fonctionnalités principales : l'encapsulation, l'héritage et le polymorphisme. Ces trois fonctionnalités optimisent l’orientation objet sous de nombreux aspects. Ces trois caractéristiques sont également des problèmes à prendre en compte lors du développement orienté objet.
Qu'est-ce que l'encapsulation en termes orientés objet ?
Encapsulation : encapsulez ensemble les données abstraites et les opérations sur les données. Les autres parties du programme ne peuvent opérer sur les données que par le biais d'opérations autorisées (méthodes membres).
L'abstraction est mentionnée ci-dessus, qui consiste à extraire les attributs et comportements communs (méthodes) d'une classe de choses pour former un modèle (classe). Cette méthode d'étude des problèmes est appelée abstraction.
Tout comme notre compte bancaire, quel que soit le compte dont il s'agit, il comprend le numéro de compte et le mot de passe, et il existe également des méthodes courantes pour retirer de l'argent, déposer de l'argent et vérifier le solde. Notre idée d'utiliser l'encapsulation est la suivante :
<?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');
Le code ci-dessus est l'encapsulation des activités bancaires, via le code d'encapsulation. Lorsque nous opérons, il nous suffit d'appeler les méthodes fournies à l'intérieur, sans avoir à gérer la façon dont les affaires sont gérées à l'intérieur. C’est une réflexion importante. La façon dont nous implémentons l'encapsulation consiste à utiliser des modificateurs d'accès et à utiliser les caractéristiques d'accès des modificateurs d'accès pour encapsuler des propriétés ou des méthodes dont l'utilisation en externe n'est pas autorisée. Dans la section précédente, nous avons parlé de trois modificateurs d'accès.
Alors, comment pouvons-nous accéder aux propriétés protégées et privées des membres ? Il existe trois méthodes ici.
Le premier
utilise les méthodes magiques __get() et __set() pour accéder
<?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; ......结果...... 小花
Nous utilisons les méthodes magiques set et get attribut pour modifier la valeur d'un attribut protégé, mais cette méthode n'est pas recommandée. Parce qu'il n'est pas assez flexible, il ne peut pas vérifier les données entrantes. La méthode suivante peut vérifier les données entrantes.
Le deuxième type
fournit une paire de méthodes getXxx() et setXxx() pour chaque variable membre privée ou protégée.
<?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'); ......结果...... 小花
En utilisant cette méthode, les données entrantes peuvent être jugées. Nous recommandons cette méthode.
Le troisième type
utilise une méthode unifiée pour opérer sur les attributs.
<?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
Parmi ces trois méthodes, nous utilisons plus souvent les deuxième et troisième méthodes en développement. Lorsque vous opérez sur un seul attribut, vous pouvez utiliser la deuxième méthode. Lorsque vous opérez sur plusieurs attributs, vous pouvez. utilisez la troisième méthode.
Il existe trop de cas où l'héritage est utilisé dans le développement. L'émergence de l'héritage réduit la redondance du code. Le code semble plus clair. Alors, qu’est-ce que l’héritage ?
<?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();
Dans le code ci-dessus, nous pouvons voir que les deux classes ont les mêmes propriétés et méthodes. S'il y a beaucoup de codes de ce type dans notre code, cela entraînera une redondance du code, et plus encore. Ce n'est pas propice à notre maintenance. La meilleure façon de résoudre ce problème est d’utiliser l’héritage pour améliorer la réutilisabilité du code.
Syntaxe héritée :
class 方法名 extends 父类的方法名{ }
Héritée en utilisant le mot-clé extends. Cela peut être compris comme une réutilisation de code, ce qui rend notre programmation plus proche de la pensée humaine. Lorsque plusieurs classes ont les mêmes attributs (variables) et méthodes, nous pouvons extraire la classe parent de ces classes et définir ces mêmes propriétés dans les attributs de la classe parent. et méthodes, toutes les sous-classes n'ont pas besoin de redéfinir ces attributs et méthodes, elles doivent seulement hériter de la classe parent
<?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();
Vous pouvez voir que les mêmes attributs et méthodes sont exportés vers Dans la classe Student (parent classe), nous écrivons deux classes supplémentaires pour hériter de la classe parent via le mot-clé extends.
La classe utilisée pour hériter est appelée classe parent, et la classe qui hérite de cette classe est appelée classe enfant.
Tant que nous héritons, nous pouvons utiliser les propriétés et les méthodes de la classe parent. Mais si l'attribut ou la méthode de la classe parent est modifié par private, la sous-classe ne peut pas hériter de . C'est la différence entre protected et private.
La sous-classe obtient les attributs et les méthodes de la classe parent par héritage, cela signifie-t-il donc qu'une copie du code de la classe parent est attribuée à la sous-classe ? Pas vraiment. L'héritage ne consiste pas simplement à copier les définitions d'attributs et de méthodes de la classe parent vers la sous-classe, mais à établir une relation de recherche. Lors de notre prochaine visite, cette relation de recherche est la suivante :
Lorsqu'un objet exploite des attributs et des méthodes, vérifiez d'abord s'il existe des attributs et des méthodes correspondants dans cette classe. Si Si oui, jugez s'il y en a. est une autorisation d'accès. S'il est accessible, accédez-y. S'il n'est pas accessible, une erreur sera signalée
Lorsqu'un objet exploite des propriétés et des méthodes, regardez-les d'abord dans ceci. class Y a-t-il des attributs et des méthodes correspondants ?Sinon, il recherchera sa propre classe parente, si elle est accessible.Si elle est accessible, elle sera accessible. ce n’est pas le cas, une erreur sera signalée.
Cette logique de recherche va à la classe de niveau supérieur. . . . .
Il y a encore beaucoup de choses auxquelles il faut faire attention en matière de succession :
子类最多只能继承一个父类(指直接继承),这和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)!