陈力:传智播客古代 珍宝币 泡泡龙游戏开发第34讲:PHP 封装、继承、多态
封装就是把抽象出的数据和对数据的操作封装在一起,通过public、 protected和private进行修饰。继承可以解决代码复用,“重载”是类的多态的一种实现。贵阳网站建设的朋友必须要掌握php面向对象编程的三大特征:封装、继承、多态。本节陈力:传智播客古代 珍宝币 泡泡龙游戏开发第34讲:PHP 封装、继承、多态。
一、PHP程序设计语言的抽象
我们在前面去定义一个类时候,实际上就是把一类事物的共有的属性和行为提取出来,形成一个物理模型(模板)。这种研究问题的方法称为抽象。
class Account {
public $blance;
public $no;
public function qun(){
}
public function cun(){
}
}
二、PHP程序设计语言的封装
封装就是把抽象出的数据和对数据的操作封装在一起,数据被保护在内部,程序的其它部分只有通过被授权的操作(成员方法),才能对数据进行操作。
那么在php中如何实现这种类似的控制呢?请大家看一个小程序。不能随便查看人的年龄,工资等隐私...
class Person{
public $name;
private $age;
private $salary;
function __construct($name,$age,$salary){
$this->name=$name;
$this->age=$age;
$this->salary=$salary
}
}
//创建一个人对象:
$p1=new Person(“宋江”,30,1500);
//查看薪水
$p1->salary;
//访问时出错,提示不能访问私有属性。
Php程序设计语言提供了三种访问控制符号:
Public:表示全局,可以在本类,类外部,子类 中使用。
Protected:表示受到保护,可以在本类、子类中使用。
Private:表示私有,只能在本类使用。
这三个控制修饰符号可以对属性和方法修饰。
如果一个方法没有访问修饰符,则默认是public;属性必须指定访问修饰符。
在类中,方法之间可以互相调用,但是需要使用 $this引用。
function test11(){
$this->test12();
}
protected function test12(){
echo "test12 hello";
}
举例说明:
class Person{
public $name;
protected $age;
private $salary;
function __construct($name,$age,$salary){
$this->name=$name;
$this->age=$age;
$this->salary=$salary;
}
//成员方法
function showInfo(){
//说明在本类中可以使用 public protected和private修饰的变量。
echo $this->name."||".$this->age."||".$this->salary;
}
}
$p1=new Person("孙悟空",30,1000); //创建一个人
echo $p1->name; //可以访问
echo $p1->age; //报错
echo $p1->salary;//报错。
我们现在就想去访问protected 变量或者 private 变量,通常的做法是提供public 函数去访问这些变量。形式是
public function setXxx($val){
}
public function getXxx(){
}
1)查看薪水 (private )
//获取这个人的薪水
public function getSalary($user,$pass){
if($user=="admin" && $pass="123"){
return $this->salary;
}else{
return "对不起,你没有权限看";
}
}
2)修改人的年龄
提供了两个函数.
//修改年龄,对输入的值,进行校验.
public function setAge($age){
if($age>1 && $age<120){
$this->age=$age;
}else {
echo "年龄范围不对";
}
}
//查看年龄
public function getAge(){
return $this->age;
}
PHP为我们提供了一对方法:__set() , __get()
__set() 对protected 或是 private属性,进行赋值操作
__set($name,$value)
__get()获取protected 或者private属性的值.
__get($name)
例子:
**用一对__set和__get方法就可以处理所有的private 和 protected属性,这种方式简单但是不够灵活,比如我要求在设置age的时候,如果age不在 1-150之间,就提示错误
**__set 和 __get就显得不方法,所以我们建议大家使用自定义set和get方法,更加灵活,可读性也好.
<?php
class Myfun{
protected $name;
private $age;
function __set($pro_name,$pro_value){
$this->$pro_name = $pro_value;
}
function __get($pro_name){
if(isset($pro_name)){
return $this->$pro_name;
}else{
return null;
}
}
}
$m = new Myfun();
$m->name = "张三3";
echo "你的名字:".$m->name;
$m->age=89;
echo "你的年龄是".$m->age;
echo "<hr>";
//自定义set 和 get方法.
class Test{
private $name;
public function getName(){
return $this->name;
}
public function setName($name){
$this->name = $name;
}
}
$t = new Test();
$t->setName("lisi");
echo $t->getName();
?>
这种形式通过一对 __set 和 __get方法来操作我们的变量,不推荐。
class A{
private $n1;
private $n2;
private $n3;
//使用__set 方法来管理所有的属性
public function __set($pro_name,$pro_val){
$this->pro_name=$pro_val;
}
//使用__get 可以获取所有的属性值
public function __get($pro_name){
if(isset($pro_name)){
return $this->pro_name;
}else{
return null;
}
}
}
$a1=new A();
$a1->n1="hello";//报错
echo $a1->n1;
$a1->n2=890;
echo "||".$a1->n2;
可以直接写一个方法了操作变量
class Peron{
protected n1;
private $n2;
function showInfo($n1){
$this->n1=$n1;
echo $this->n1.”||”$this->n2;
}
}
三、PHP程序设计语言的继承
继承可以解决代码复用,让我们的编程更加靠近人类思维。当多个类存在相同的属性(变量)和方法时,可以从这些类中抽象出父类(比如刚才的Student),在父类中定义这些相同的属性和方法,所有的子类不需要重新定义这些属性和方法,只需要通过
class 子类名 extends 父类名{
}
这样,子类就会自动拥有父类定义的属性和方法。
我们对extend1.php 改进,使用继承的方法,
请大家注意体会使用继承的好处.
//应用场景 开发一套学生管理系统(Pupil, Graduate , ...)
class Pupil{
public $name;
protected $age;
protected $grade;
public function showInfo(){
echo $this->name."||".$this->age;
}
public function tesing(){
echo " 小学生考试..";
}
}
class Graduate{
public $name;
protected $age;
protected $grade;
public function showInfo(){
echo $this->name."||".$this->age;
}
public function tesing(){
echo "研究生考试..";
}
}
从上面的代码我们看出,代码的复用性不高,同时也会让管理代码成本增高.
解决之道(继承);
在我们使用继承对上面的代码,进行改写.
代码:
//应用场景 开发一套学生管理系统(Pupil, Graduate , ...)
//父类
class Stu{
public $name;
protected $age;
protected $grade;
public function showInfo(){
echo $this->name;
}
}
//子类
class Pupil extends Stu{
public function testing(){
echo " 小学生考试..";
}
}
class Graduate extends Stu{
public function testing(){
echo "研究生考试..";
}
}
//创建学生
$stu1=new Pupil();
$stu1->name="小明";
$stu1->testing();
$stu1->showInfo();
//创建一个研究生
$stu2=new Graduate();
$stu2->name="老明";
$stu2->testing();
$stu2->showInfo();
从上面的代码我们可以看出:
所谓继承就是一个子类通过 extends 父类 把父类的 (public / protected)属性和(public / protected)方法继承下来。
继承的基本语法是:
class 类名 extends 父类名{
//在写自己需要的属性和方法.
}
继承细节:
(1)父类的 public 、protected 的属性和方法被继承. private 的属性和方法没有被继承。
(2)一个类只能继承一个父类,(直接继承).如果你希望继承多个类的属性和方法,则使用多层继承。
class A{
public $n1=90;
}
class B extends A{
public $n2="hello";
}
class C extends B{}
$c=new C();
echo $c->n1;
(3)当创建子类对象的时候,默认情况下不会自动调用父类的构造方法。
class A{
public $n1=90;
//构造方法
public function __construct(){
echo "A __construct";
}
}
class B extends A{
function __construct(){
echo "B __construct";
}
}
//创建B对象实例
$b=new B();
(4)如果我们希望去调用父类的构造方法,或者其它的方法(public / protected),可以这样处理 :
类名::方法名() 或 parent::方法名()
举例
class A{
public $n1=90;
//构造方法
public function __construct(){
echo "A __construct";
}
}
class B extends A{
function __construct(){
echo "B __construct";
//显式的调用父类的方法
//A::__construct();
parent::__construct();
}
}
//创建B对象实例
$b=new B();
(5)当一个子类的方法和父类的方法完全一样(public protected), 我们称为 方法的覆盖 (方法重写)。
①子类最多只能继承一个父类(指直接继承)
②子类可以继承其父类(或者超类)的 public ,protected修饰的变量(属性) 和 函数(方法).
③在创建某个子类对象时,默认情况下不会自动调用其父类的构造函数,这点与java是不一样的。
④如果在子类中需要访问其父类的方法(构造方法/成员方法 方法的访问修饰符是public/protected),可以使用父类::方法名
(或者 parent::方法名) 来完成。
⑤如果子类(派生类)中的方法和父类(基类)方法相同,我们称为方法重写/方法覆盖]。
实例:
1). 判断下面代码是否正确
<?php
class A1 {
public $n;
protected $k;
}
class A2 {
}
class A3 extends A1,A2{
public function test1(){
echo "hello";
}
}
?>
错误,不能多重继承。
2). 判断下面的说法是不是正确
a. 子类可以继承父类的public 属性,private 方法。错误。
b. 子类不能继承父类的private 属性,但是可以继承private方法。错误。
c. 子类只能继承父类的public protected属性,但不能继承protected 方法。错误。
3). 下面的代码会输出什么内容
(1)
class Stu{
public function __construct(){
echo "stu()";
}
}
class Pupil extends Stu{
public function __construct(){
Stu::__construct();
}
}
$pupil=new Pupil();
输出:stu()
(2)
class Stu{
public function __construct(){
echo "stu()";
}
}
class Pupil extends Stu{
public function __construct(){
PARENT::__construct();
}
}
$pupil=new Pupil();
代码错误,不能大写;
(3)
class A{
public function __construct(){
echo "A()";
}
}
class B extends A{
public function __construct(){
parent::__construct();
}
}
class C extends B{
public function __construct(){
parent::__construct();
}
}
$c=new C();
输出:A()
(4)
class A{
public function __construct(){
echo "A()";
}
private test(){
echo "test()";
}
}
class B extends A{
public function __construct(){
parent::test();
}
}
$b=new B();
输出:代码错误,A类中private test(){
为私有的。不能调用。
四、PHP程序设计语言的多态
“重载”是类的多态的一种实现。函数重载指一个标识符被用作多个函数名,且能够通过函数的参数个数或参数类型将这些同名的函数区分开来,调用不发生混淆。调用的时候,虽然方法名字相同,但根据参数表可以自动调用对应的函数。
PHP5 虽然可以支持重载,但重载在具体实现上,和其他语言还有较大的差别。如果你试图定义多个同名函数(如下),那你的程序运行时将会报错,请看下面的一个案例:
class phpClass{
public function __construct(){
//do something}
public funciton __construct($arg1){
//do something}
}
势图定义两个构造函数。
class phpClass2{
public function test1(){
//do something}
public funciton test1($arg1){
//do something}
}
势图定义两个普通函数。
两个案例,均会报错,结论: 构造函数和成员函数,都不能重载.(创建实例,就会出效果,因为php的解释执行.)
基本概念:函数名一样,通过函数的参数个数或者是参数类型不同,达到调用同一个函数名,但是可以区分不同的函数。
class A{
public function test1(){
echo “test1”;
}
public function test1($a){
echo “test1 hello”;
}
}
重载:
$a =new A();
$a->test1();
$a->test1(456);
上面的这用用法是不对,PHP会提示错误。
PHP5程序设计语言中如何通过魔术函数(__call)来实现方法重载的效果。
PHP5程序设计语言给我们提供了强大的‘魔术’函数, 使用这些‘魔术’函数, 我们也可以做到函数重载. 这里要用到函数是 __call。
php程序设计语言中有以下魔术函数:__set __get __construct __destruct __call __toString __clone __sleep __wakeup __isset __unset __autoload
php程序设计语言中有以下魔术常量:__LINE__ (所在行), __FILE__(文件绝对路径还文件名),__FUNCTION__ , __CLASS__ ,__METHOD__
代码说明:
<?php
class A{
public function test1($p){
echo "接收一个参数";
echo "<br/>接收到参数是";
var_dump($p);
}
public function test2($p){
echo "接收两个参数<br/>";
var_dump($p);
}
//这些提供一个__call
//__call 是它一个对象调用某个方法,而该方法不存在,则系统会自动调用__call
function __call($method,$p){
var_dump($p);
if($method=="test"){
if(count($p)==1){
$this->test1($p);
}else if(count($p)==2){
$this->test2($p);
}
}
}
}
$a=new A();
$a->test(1);//输出:接收一个参数。
$a->test(56,90); //输出:接收两个参数。
?>
小结: php5程序设计语言默认情况下不直接支持方法重载,可以同__call魔术方法,模拟一个方法效果的效果。
五、PHP程序设计语言的方法重写/方法覆盖(override)
既然子类可以继承父类的属性和方法,这样可以提高代码的复用性,这个很好,可以问题来了,假设现在我要求大家写三个类猫猫、狗狗、猪猪。我们知道这三个东东都是动物,动物必然存在相同的特点。根据类的抽象特征,我们可以把它们的相同点提取出来,形成一个父类 Animal,然后继承。
当一个父类知道所有的子类都有一个方法,但是父类不能确定该方法究竟如何写,可以让子类去覆盖这个方法,具体用法如下:
<?php
//抽象一个Animal类
class Animal{
public $name;
protected $price;
function cry(){
echo "不知道动物怎么叫..";
}
}
class Dog extends Animal{
//覆盖
function cry(){
echo "小狗汪汪叫..";
}
}
class Pig extends Animal{
//覆盖
function cry(){
echo "哼哼叫..";
}
}
//创建一个只狗
$dog1=new Dog();
$dog1->cry();
$pig1=new Pig();
$pig1->cry();
?>
简单的说:方法重写就是子类有一个方法,和父类的某个方法的名称、参数个数一样(但是并不要求参数的名称一样),那么我们就说子类的这个方法重写了父类的那个方法。比如上个案例的 Cat 类中的 cry方法就重写/覆盖了Animal类的cry方法。
如果在子类中需要访问其父类的方法(public/protected属性),可以使用“父类::方法名”或者“parent::方法名”来完成。
在实现方法覆盖的时候,访问修饰符可以不一样 //但是必须满足: 子类的访问范围>=父类的访问范围。
多态体现在什么地方?
class Animal{
public function cry(){
echo '动物叫唤..';
}
}
class Cat extends Animal{
public function cry(){
echo "abc";
}
}
$cat1=new Cat();
$cat1->cry();
当子类没有覆盖父类的方法则 $cat1->cry() 调用的是父类,当子类覆盖了父类的方法,则调用自己的cry()。
class Animal{
public function cry(){
echo '动物叫唤..';}}
class Cat extends Animal{
protected function cry(){
echo '猫猫叫唤..';Animal::cry();
}}
$cat1=new Cat();
结论: 报错
如果把Animal 的 cry() 也改成 protected 则允许可以成功
或者把Animal 改成protected 而Cat是public也可以运行成功!
【推荐阅读】
陈力:传智播客古代 珍宝币 泡泡龙游戏开发第34讲:PHP 封装、继承、多态