1.面向对象
类是属性和方法的集合, PHP的对象是用数组来模拟的。
序列化对象后,存储的只是对象的属性。类是由属性和方法组成的,而对象则是属性的集合。由同一个类生成的不同对象,拥有不同的属性,
但共享了类的代码空间中方法区域的代码。
在PHP5中,对象在底层的实现是采用 '属性数组 + 方法数组' 来实现的。
对象的组成 :
1.属性数组
2.类指针
1.类属性
2.静态属性
3.类常量
4.标准方法
5.魔术方法
6.自定义方法列表
对象与数组的区别在于:对象还有个指针,指向了它所属的类。
序列化后的对象会附带所属的类名,这个类名保证此对象能够在执行类的方法时,能够正确的找到方法所在的代码空间(即对象所拥有的方法存储在
类里面)。另外,当一个对象的实例变量引用其他对象时,序列化该对象时也会对引用对象进行序列化。
1.类是定义一些列属性和操作的模板,而对象则把属性进行具体化,然后交给类处理。
2.对象就是数据,对象本身不包含方法。但是对象有一个'指针'指向一个类,这个类里可以有方法。
3.方法描述不同属性所导致的不同表现。
4.类和对象是不可分割的,有对象就必定有一个类和其对应。
耦合是一个软件结构内不同模块之间互连程度的度量,也就是不同模块之间的依赖关系。
解耦是要接触模块与模块之间的依赖。
继承与组合更倾向使用组合,为什么呢?
1.继承破坏封装性
鸟类为父类,但其子类鸭子等不需要飞翔这个方法,但却可以无区别的使用。
2.继承是耦合的
继承使得子类和父类捆绑在一起,组合仅通过唯一接口和外部进行通信。
3.继承扩展复杂
随着继承层数的增加和子类的增加,将涉及大量方法重写。使用组合,可以根据类型约束,实现动态组合,减少代码。
4.不恰当的使用继承可能违反现实世界的逻辑
组合的缺点:
在创建组合对象的时候,组合需要一一创建局部对象。组合比继承增加了代码量。
继承最大的优点是扩展简单。
如何使用继承?
1.精心设计专门用于被继承的类,继承树的抽象层应该比较稳定,一般不要多于3层。
2.对于不是专门用于被继承的类,禁止其被继承,也就是使用 final 修饰符。使用 final 修饰符即可防止重要方法被非法覆盖,又能给编译器
寻找优化的机会。
1.优先考虑组合关系提高代码的可重用性
2.子类是一个特殊的类型,而不只是父类的一个角色
3.子类扩展,而不是覆盖或者使用父类的功能失效
4.底层代码多用组合,顶层/业务层代码多用继承。底层用组合可以提高效率,避免对象臃肿。顶层代码用继承可以提高灵活性,让业务更方便。
如果既要组合的灵活,又要继承的代码简洁?
使用多重继承。一个类可以同时继承多个父类,组合两个父类的功能。
PHP 5.4 引入的 traits 可以方便我们实现对象的扩展,是除 extend, implements 外的另外一种扩展对象的方式。
2.多态
同一类的对象收到相同消息时,会得到不同的结果。
多态性是一种通过多种状态或阶段描述相同对象的编程方式。它真正的意义在于:实际开发中,只要关系一个接口或基类的编程,而不必关心一个对象所属的
具体类。PHP 作为一门脚本语言,本身就是多态的。
C++ 中的多态,定义一个父类,然后定义一个子类,这个子类继承父类的一些方法并且有自己的方法。通过 father *pf = new son; 语句创建一个
派生类(子类)对象,并且把该派生类对象赋值给基类(父类)指针,然后用该指针访问父类中的 eat 和 run 方法。
由于父类中的 run 方法加了 virtual 关键字,表示该函数有多种状态,可能被多个对象所拥有。也就是说,多个对象在调用同一名字的函数时会产生不同的
效果。
和 PHP 有什么不同呢?C++ 的这个例子所创建的对象是一个指向父类的子对象,还可以创建更多的派生类,然后上转型为父类对象。这些对象,都是同一类对象,
但是在运行时,却都能调用到派生类同名函数。而PHP中的例子则是不同类的对象调用。
由于PHP是弱类型,并且也没有对象转型机制,所有不能像C++那样实现 father *pf = new son; 把派生类对象赋给基类对象,然后在调用的时候动态的改变
其指向。在PHP中,对象是确定的,是不同类的对象。所以,从这个角度看,不是真正的多态。
区别多态的关键在于看对象是否属于同一类型。如果把它们看做同一种类型,调用了相同函数,返回了不同结果,那么它就是多态。否则,不是称其为多态。
1.多态指同一类对象在运行时的具体化
2.PHP语言是弱类型,实现多态更简单
3.类型转换不是多态
4.PHP中父类和子类看做 '继父'和'继子' 关系,它们存在继承关系,不存在血缘关系。因此子类无法向上转型为父类,从而失去多态的最典型特征。
5.多态的本质就是 if...else,只不过实现的层级不同。
3.面向接口编程
接口定义一套规范,描述一个'物'的功能。
采用一个特定接口的所有代码都知道对于那个接口会调用什么方法。这便是接口的全部含义。接口通常作为类与类之间的一个'协议'。接口是抽象类的变体,
接口中所有的方法都是抽象的,没有一个程序体。接口除了可以包含方法外,还可以包含常量。
接口为抽象而生,接口是一种契约。
大型项目中,核心开发人员和技术经理编写核心的流程和代码,以接口形式给出。而基础开发人员,指针对这些接口,填充代码,如数据库操作等。这样,
核心人员把更多精力投入到了技术公关和业务逻辑中。前端只针对接口编程,只管在 Action 层调用 Service,不管实现细节。而后端则负责Dao和 Service
层接口的实现。
为什么一个类实现了 Iterator 迭代器,其对象就可以被用作 foreach 呢。原因是,在对 PHP 实例对象使用 foreach 时,会检查这个实例有没有实现
Iterator 接口,如果实现了,就会通过内置方法或者使用实现类中的方法模拟 foreach语句。这和 __toString() 很像,事实上,__toString()方法就是
接口的一种变相实现。
接口是对多重继承的一种变相实现,trait 可以被视为一种加强型接口。
trait 和接口很像,不同的是 trait 是可以导入包含代码的接口。
4.反射
面向对象编程中对象被赋予了自身的能力,而这个自身的过程就是反射。
反射,直观理解就是根据到达地找到出发地和来源。
反射指在 PHP 运行状态中,扩展分析 PHP 程序,到处或提取关于类,方法,属性,参数等详细信息,包括注释。这种动态获取信息以及动态调用对象方法
的功能被称为反射 API。
反射的作用:
反射可以用于生成文档。因此它可以对文件里的类进行扫描,逐个生成描述文档。
既然反射可以探知类的内部结构,那么是不是可以用它做 hook 实现插件功能?或者是做动态代理呢?
<?php
class mysql
{
public function connect($db)
{
echo '连接到数据库 db';
}
}
class sqlproxy
{
private $target;
public function __construct($tar)
{
$this->target[] = new $tar();
}
public function __call($name, $arguments)
{
foreach ($this->target as $obj) {
$r = new ReflectionClass($obj);
if ($method = $r->getMethod($name)) {
if ($method->isPublic() && !$method->isAbstract()) {
echo '方法前拦截记录';
$method->invoke($obj, $args);
echo '方法后拦截';
}
}
}
}
}
$obj = new sqlproxy('mysql');
$obj->connect('member');
这里真正操作的类是 mysql,但是 sqlproxy 类实现了根据动态传入参数,代替实际的类运行,并且在方法前后进行拦截,并且动态的改变了方法和属性。这就是
简单的动态代理。
反射的消耗很大。反射可以保持代码的优雅和简洁,但反射也会破坏类的封装性,因为暴露了方法和属性。
5.错误和异常
PHP 里的异常,是程序运行中不符合预期的情况以及正常流程不同的状况。异常和错误是2个不同的概念。
PHP 只有手动抛出异常后才能捕获异常。
下面3种场景用到异常:
1.对程序的悲观预测
2.程序的需要和对业务的关注
3.语言级别的健壮性要求
PHP 的错误级别:
1.deprecated
2.notice
3.warning
4.fetal
5.parse error
set_error_handler();
trigger_error();
1.1 对象的本