PHP高级语法(三)—— 面向对象(中)

4 封装


封装性是面向对象编程中的三大特性之一,封装性就是把对象的属性和服务结合成一个独立的相同单位,并尽可能隐蔽对象的内部细节,使多个系统模块对象形成一个松耦合的关系。封装包含两个含义:1.把对象的全部属性和全部服务结合在一起,形成一个不可分割的独立单位(即对象)。2.信息隐蔽,即尽可能隐蔽对象的内部细节,对外形成一个边界〔或者说形成一道屏障〕,只保留有限的对外接口使之与外部发生联系。

PHP使用private 这个关键字来对属性和方法进行封装:

原来的成员:
var $name; //声明人的姓名
var $sex; //声明人的性别
var $age; //声明人的年龄
function run(){… … .}

改成封装的形式:
private $name; //把人的姓名使用private 关键字进行封装
private $sex; //把人的性别使用private 关键字进行封装
private $age; //把人的年龄使用private 关键字进行封装
private function run(){… … } //把人的走路方法使用private 关键字进行封装

注意:只要是成员属性前面有其它的关键字就要去掉原有的关键字“var”。

通过private 就可以把人的成员(成员属性和成员方法)封装上了。封装上的成员就不能被类外面的对象直接访问了,只有对象内部自己可以访问;下面的代码会产生错误:
$p1=new Person();
echo $p1->name."<br>";
echo $p1->sex."<br>";
echo $p1->age."<br>";
$p1->run();

注意,默认的成员属性或方法是公有的,所以我们在类的外部调用say()方法是可以的。下面就看一个关于封装性实例:
<?php
class Person
{


//下面是人的成员属性
private $name; //人的名字,被private封装上了
private $sex; //人的性别, 被private封装上了
private $age; //人的年龄, 被private封装上了


//定义一个构造方法参数为私有的属性姓名$name、性别$sex和年龄$age进行赋值
function __construct($name, $sex, $age)
{
//通过构造方法传进来的$name给私有成员属性$this->name赋初使值
$this->name=$name;
//通过构造方法传进来的$sex给私有成员属性$this->sex赋初使值
$this->sex=$sex;
//通过构造方法传进来的$age给私有成员属性$this->age赋初使值
$this->age=$age;
}

 

//这个人可以说话的方法, 说出自己的私有属性,在这里也可以访问私有方法
function say()
{
echo "我的名字叫:".$this->name." 性别:".$this->sex." 我的年龄是:".$this->age."<br>";
}


}


//通过构造方法创建3个对象$p1、p2、$p3,分别传入三个不同的实参为姓名、性别和年龄
$p1=new Person(“张三”,”男”, 20);
$p2=new Person(“李四”,”女”, 30);
$p3=new Person(“王五”,”男”, 40);
//下面访问$p1对象中的说话方法
$p1->say();
//下面访问$p2对象中的说话方法
$p2->say();
//下面访问$p3对象中的说话方法
$p3->say();


%>

5 __set() __get() __isset() __unset()四方法


我们鼓励,总是把类的属性定义为private,因为这更符合现实的逻辑。但是,对属性的读取和赋值操作是非常频繁的,因此在PHP5 中,预定义了两个函数“__get()”和“__set()”来获取和赋值其属性,以及检查属性的“__isset()”和删除属性的方法“__unset()”

上一节中,我们为每个属性做了设置和获取的方法,在PHP5 中给我们提供了专门为属性设置值和获取值的方法,“__set()”、“__get()”等这四个方法,像构造方法(__construct())一样, 是系统提供的。但是,除了构造方法外,只有在类里面添加了这些方法,才能在内外部通过对象调用这些方法。系统提供的方法是:get、set、isset、unset。我们可以按下面的方式来添加这两个方法,推荐在这些函数前添加两个下划线“_”,当然也可以按个人的风格来添加:

//__get()方法用来获取私有属性
private function __get($property_name)
{

if(isset($this->$property_name))
{
          return($this->$property_name);
}else
{
          return(NULL);
}


}

//__set()方法用来设置私有属性
private function __set($property_name, $value)
{
         $this->$property_name = $value;
}

我们来看一个完整的实例:
<?php
class Person
{


//下面是人的成员属性
private $name; //人的名字
private $sex; //人的性别
private $age; //人的年龄


//__get()方法用来获取私有属性
private function __get($property_name)
{
         if(isset($this->$property_name)){
                  return($this->$property_name);
         }else {
                  return(NULL);
         }
}

 

//__set()方法用来设置私有属性
private function __set($property_name, $value)
{
          $this->$property_name = $value;
}

 

//__isset()方法
private function __isset($nm)
{
          echo "isset()函数测定私有成员时,自动调用<br>";
          return isset($this->$nm);
}

 

//__unset()方法
private function __unset($nm)
{
         echo "当在类外部使用unset()函数来删除私有成员时自动调用的<br>";
         unset($this->$nm);
}


}


$p1=new Person();
$p1->name="this is a person name";
//在使用isset()函数测定私有成员时,自动调用__isset()方法帮我们完成,返回结果为true
echo var_dump(isset($p1->name))."<br>";
echo $p1->name."<br>";
//在使用unset()函数删除私有成员时,自动调用__unset()方法帮我们完成,删除name私有属性
unset($p1->name);
//已经被删除了, 所这行不会有输出
echo $p1->name;
?>

输出结果为:
isset()函数测定私有成员时,自动调用
bool(true)
this is a person name
当在类外部使用unset()函数来删除私有成员时自动调用的

__set()、__get()、__isset()、__unset() 这四个方法都是我们添加到对象里面的,在需要时自动调用的,来完成在对象外部对对象内部私有属性的操作。

6 继承


在PHP 和Java 语言里面没有多继承,只有单继承,也就是说,一个类只能直接从一个类中继承数据,这就是我们所说的单继承。例如定义一个子类“学生类“使用”extends”关键字来继承”人”类:
class Student extends Person{

var $school; //学生所在学校的属性
//这个学生学习的方法
function study()
{
         echo "我的名字叫:".$this->name." 我正在”.$this->school.”学习<br>";
}


}

通过上面类继承的使用简化了对象、类的创建工作量,增加了代码的可重性。但是从上面这一个例子上中“可重用性”以及其它的继承性所带来的影响,我们看的还不是特别的明显,你扩展的去想一下,人有无数个岗位,比如上面的学生还有老师、工程师、医生、工人等,很多很多,如果每个类都定义“人”都共同具有的属性和方法,想一想会有很大的工作量,这些属性和方法都可以从“Person”人类里面继承过来。

7 方法的重载


PHP 中的方法是不能按照C++或者java中的那个意义进行重载,所谓的方法重载就是定义相同的方法名,通过“参数的个数”不同或“参数的类型”不同,来访问我们的相同方法名的不同方法。但是因为PHP 是弱类型的语言,所以在方法的参数中本身就可以接收不同类型的数据,又因为PHP的方法可以接收不定个数的参数,所以通过传递不同个数的参数调用不相同方法名的不同方法也是不成立的。所以在你的项目中不能定义相同方法名的方法。另外,因为PHP 没有名字空间的概念,在同一个页面和被包含的页面中不能定义相同名称的方法,也不能定义和PHP 自身提供的方法重名,当然在同一个类中也就不能定义相同名称的方法。

我们这里所指的重载新的方法所指的是什么呢?其实我们所说的重载新的方法就是子类覆盖父类的已有的方法,那为什么要这么做呢?父类的方法不是可以继承过来直接用吗?但有一些情况是我们必须要覆盖的,比如说我们前面提到过的例子里面,“Person”这个人类里面有一个“说话”的方法,所有继承“Person”类的子类都是可以“说话”的, 我们“Student”类就是“Person”类的子类,所以“Student”的实例就可以“说话”了,但是人类里面“说话”的方法里面说出的是“Person”类里面的属性,而“Student”类对“Person”类进行了扩展,又扩展出了几个新的属性,如果使用继承过来的“say()”说话方法的话,只能说出从“Person”类继承过来的那些属性,那么新扩展的那些属性使用这个继承过来的“say()”的方法就说不出来了,那有的人就问了,我在“Student”这个子类中再定义一个新的方法用于说话,说出子类里面所有的属性不就行了吗?一定不要这么做,从抽象的角度来讲, 一个“学生”不能有两种“说话”的方法,就算你定义了两个不同的说话的方法,可以实现你想要的功能,被继承过来的那个“说话”方法可能没有机会用到了,而且是继承过来的你也删不掉。这个时候我们就要用到覆盖了,这也就是所谓的“多态”。

虽然说在PHP 里面不能定义同名的方法, 但是在父子关系的两个类中,我们可以在子类中定义和父类同名的方法,这样就把父类中继承过来的方法覆盖掉了:

//这个学性可以说话的方法, 说出自己所有的属性,覆盖了父类的同名方法
function say()
{
echo "我的名字叫:".$this->name." 性别:".$this->sex." 我的年龄是:".$this->age." 我在".$this->school."上学.<br>";
}

但是,像这样做虽然解决了我们上面说的问题,但是在实际开发中,一个方法不可能就一条代码或是几条代码,比如说“Person”类里面的“say()”方法有里面有100 条代码,如果我们想对这个方法覆盖保留原有的功能外加上一点点功能,就要把原有的100 条代码重写一次, 再加上扩展的几条代码,这还算是好的,而有的情况,父类中的方法是看不见原代码的,这个时候你怎么去重写原有的代码呢?我们也有解决的办法,就是在子类的这个方法中可以调用到父类中被覆盖的方法, 也就是把被覆盖的方法原有的功能拿过来再加上自己的一点功能,可以通过两种方法实现在子类的方法中调用父类被覆盖的方法:

一种是使用父类的“类名::”来调用父类中被覆盖的方法;
一种是使用“parent::”的方试来调用父类中被覆盖的方法;

<?php
class Student extends Person
{
var $school; //学生所在学校的属性


//这个学生学习的方法
function study()
{
       echo "我的名字叫:".$this->name." 我正在”.$this->school.”学习<br>";
}


//这个学性可以说话的方法, 说出自己所有的属性,覆盖了父类的同名方法
function say()
{
        //使用父类的“类名::“来调用父类中被覆盖的方法;
        // Person::say();
        //或者使用“parent::”的方试来调用父类中被覆盖的方法;
        parent::say();
        //加上一点自己的功能
        echo “我的年龄是:".$this->age."我在".$this->school."上学.<br>";
}


}
?>

现在用两种方式都可以访问到父类中被覆盖的方法,我们选那种方式最好呢?用户可能会发现自己写的代码访问了父类的变量和函数。如果子类非常精炼或者父类非常专业化的时候尤其是这样。不要用代码中父类文字上的名字,应该用特殊的名字parent,它指的就是子类在extends 声明中所指的父类的名字。这样做可以避免在多个地方使用父类的名字。如果继承树在实现的过程中要修改,只要简单地修改类中extends 声明的部分。

同样,构造方法在子类中如果没有声明的话,也可以使用父类中的构造方法,如果子类中重新定义了一个构造方法也会覆盖掉父类中的构造方法,如果想使用新的构造方法为所有属性赋值也可以用同样的方式。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值