PHP面向对象--三大特性
最后更新:2018-11-16
面向对象编程,也就是我们常说的OOP,其实是面向对象的一部分。面向对象一共有3个部分:面向对象分析(OOA)、面向对象设计(OOD)、面向对象编程(OOP)。
面向对象的三大特性是"封装、"多态"、"继承",五大原则是"单一职责原则"、"开放封闭原则"、"里氏替换原则"、"依赖倒置原则"、"接口分离原则"。
一、封装
1、定义:
封装就是把抽象出的数据和对数据的操作封装在一起,数据被保护在内部,程序的其他部分只有通过被授权的操作(成员方法)才能对数据进行操作。
封装是面向对象的特征之一,是对象和类概念的主要特性。 简单的说,一个类就是一个封装了数据以及操作这些数据的代码的逻辑实体。在一个对象内部,某些代码或某些数据可以是私有的,不能被外界访问。通过这种方式,对象对内部数据提供了不同级别的保护,以防止程序中无关的部分意外的改变或错误的使用了对象的私有部分。
2、封装的作用
1)使用者只关心类能够提供的功能,而不必关心功能实现的细节!(封装方法)
2)对用户的数据进行控制,防止设置不合法数据,控制返回给用户的数据(属性封装+set/get方法)
3、如何实现封装
1)方法的封装
对于一些只在类内部使用的方法,而不像对外部提供使用。那么,这样的方法我们可以使用private进行私有化处理。
private function formatName(){} //这个方法仅仅能在类内部使用$this调用
function showName(){
$this -> formatName();
}
2)属性的封装+set/get方法
为了控制属性的设置以及读取,可以将属性进行私有化处理,并要求用户通过我们提供的set/get方法进行设置/获取
private $age;
function setAge($age){
$this->age = $age;
}
function getAge(){
return $this->age;
}
$object->getAge();
$object->setAge(12);
3)属性的封装+魔术方法
private $age;
function __get($key){
return $this->$key;
}
function __set($key,$value){
$this->$key=$value;
}
$对象->age; // 访问对象私有属性时,自动调用__get()魔术方法,并且将访问的属性名传给__get()方法;
$对象->age=12; // 设置对象私有属性时,自动调用__set()魔术方法,并且将设置的属性名以及属性值传给__set()方法;
4、关于封装的魔术方法
1)__set(value):给类私有属性赋值时自动调用,调用时给方法传递两个参数:需要设置的属性名、属性值;
2)__get($key):读取类私有属性时自动调用,调用时给方法传递一个参数:需要读取的属性名;
3)__isset($key)::当使用 isset()函数或者empty()函数 判断属性是否存在或者是否为空的时候会自动触发。
--------类外部使用isset();检测私有(private)属性,默认是检测不到的。false
--------所以,我们可以使用__isset();函数,在自动调用时,返回内部检测结果。
function __isset($key){
return isset($this->$key);
}
//当外部使用isset($对象名->私有属性);检测时,将自动调用上述__isset()返回的结果!
4)__unset($key):外部使用unset()函数删除私有属性时,自动调用;
function __unset($key){
unset($this->$key);
}
//当外部使用unset($对象名->私有属性);删除属性时,自动将属性名传给__unset(),并交由这个魔术方法处理。
二、继承
1、定义:
继承是指可以让某个类型的对象获得另一个类型的对象的属性的方法,它支持按级分类的概念。
继承是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。 通过继承创建的新类称为“子类”或“派生类”,被继承的类称为“基类”、“父类”或“超类”。继承的过程,就是从一般到特殊的过程。要实现继承,可以通过“继承”(Inheritance)和“组合”(Composition)来实现。继承概念的实现方式有二类:实现继承与接口继承。实现继承是指直接使用基类的属性和方法而无需额外编码的能力;接口继承是指仅使用属性和方法的名称、但是子类必须提供实现的能力;
2、重写(覆盖)
在子类中出现和父类同名的方法,这个称为重写,重写是行为的重新定义。重写只能发生在有继承关系的类之间,方法名称相同即重写,与参数没有关系,重写时访问修饰符的可以访问的范围不能低于父类中访问修饰符。对于private修饰的方法不能重写。
如果,子类重写了父类方法,如何在子类中调用父类同名方法?
partent::方法名();
所以,当子类继承父类时,需在子类的构造中的第一步,首先调用父类构造进行复制。
function __construct($name,$sex,$school){
parent::__construct($name,$sex);
$this->school = $school;
}
3、实现继承
给子类使用extends关键字,让子类继承父类;
class Student extends Person{}
4、注意事项
1) PHP中继承是单继承,即extends后面只能有1个类名;但是一个类进行多层继承。
2)子类继承父类的所有内容,但父类中的private部分不能直接访问。
3)子类中新增加的属性和方法是对父类的扩展。
三、多态
1、定义:
一个类被多个子类继承,如果这个类的某个方法在多个子类中表现出不同的功能,我们称这种行为为多态。
多态是指OOP 能够根据使用类的上下文来重新定义或改变类的性质或行为,或者说接口的多种不同的实现方式即为多态。把不同的子类对象都当作父类来看,可以屏蔽不同子类对象之间的差异,写出通用的代码,做出通用的编程,以适应需求的不断变化。
2、实现多态必要途径
1)子类继承父类
2)重写父类方法
3)父类引用指向子类对象;
4)调重写的方法
3、案例
interface Computer {
public function version();
public function work();
}
class NotebookComputer implements Computer {
public function version() {
echo '联想120';
}
public function work() {
echo '笔记本正在随时携带运行!';
}
}
class desktopComputer implements Computer {
public function version() {
echo 'IBM';
}
public function work() {
echo '台式电脑正在工作站运行!';
}
}
class Person {
public function run($type) {
$type->version();
$type->work();
}
}
$person = new Person();
$desktopcomputer = new desktopComputer();
$notebookcomputer = new NoteBookComputer();
$person->run($notebookcomputer);
四、其它
1、重载:
1)定义:
函数名一样,通过函数的参数个数或者参数类型不同,达到调用同一个函数名,但是可以区分不同的函数。
2)注意
PHP面向对象是不支持方法重载的,但是可以通过魔术方法__call来实现重载的目的,但不推荐使用。
3)案例
Class A {
public function test1($p) {
echo "接收一个参数<br/>";
}
public function test2($p) {
echo "接收两个参数<br/>";
}
//提供__call 它一个对象调用某个方法,而该方法不存在,则系统会自动调用__call
function __call($method,$p) {
if ($method == "test") {
if (count($p) == 1) {
$this -> test1($p);
} elseif (count($p) == 2) {
$this -> test2($p);
}
}
}
}
$a = new A();
$a -> test(1);
$a -> test(21,43)
2、抽象:
1)定义
忽略一个主题中与当前目标无关的东西,专注注意与当前目标有关的方面。(就是把现实世界中的某一类东西,提取出来,用程序代码表示,抽象出来的一般叫做类或者接口)。抽象并不打算了解全部问题,而是选择其中的一部分,暂时不用管部分细节。抽象包括两个方面,一个数据抽象,二是过程抽象。
- 数据抽象 -->表示世界中一类事物的特征,就是对象的属性.比如鸟有翅膀,羽毛等(类的属性)
- 过程抽象 -->表示世界中一类事物的行为,就是对象的行为.比如鸟会飞,会叫(类的方法)
3、面向对象五大基本原则
1)单一职责原则SRP(Single Responsibility Principle)
是指一个类的功能要单一,不能包罗万象。如同一个人一样,分配的工作不能太多,否则一天到晚虽然忙忙碌碌的,但效率却高不起来。
2)开放封闭原则OCP(Open-Close Principle)
一个模块在扩展性方面应该是开放的而在更改性方面应该是封闭的。比如:一个网络模块,原来只服务端功能,而现在要加入客户端功能,那么应当在不用修改服务端功能代码的前提下,就能够增加客户端功能的实现代码,这要求在设计之初,就应当将服务端和客户端分开,公共部分抽象出来。
3)替换原则(the Liskov Substitution Principle LSP)
子类应当可以替换父类并出现在父类能够出现的任何地方。比如:公司搞年度晚会,所有员工可以参加抽奖,那么不管是老员工还是新员工,也不管是总部员工还是外派员工,都应当可以参加抽奖,否则这公司就不和谐了。
4)依赖原则(the Dependency Inversion Principle DIP)
具体依赖抽象,上层依赖下层。假设B是较A低的模块,但B需要使用到A的功能,这个时候,B不应当直接使用A中的具体类:而应当由B定义一抽象接口,并由A来实现这个抽象接口,B只使用这个抽象接口:这样就达到了依赖倒置的目的,B也解除了对A的依赖,反过来是A依赖于B定义的抽象接口。通过上层模块难以避免依赖下层模块,假如B也直接依赖A的实现,那么就可能造成循环依赖。一个常见的问题就是编译A模块时需要直接包含到B模块的cpp文件,而编译B时同样要直接包含到A的cpp文件。
5)接口分离原则(the Interface Segregation Principle ISP)
模块间要通过抽象接口隔离开,而不是通过具体的类强耦合起来。
参考
https://blog.csdn.net/wang544831183/article/details/77322569
https://www.cnblogs.com/corvoh/p/5747856.html
http://www.runoob.com/php/php-oop.html