这些年,月小升同窗发现本身不会读书因而买了一本《如何阅读一本书》,发现本身不会作笔记就买了一本《如何作笔记》,写代码久了,发现本身一直在用的面向对象不是很了解,常常把代码写成一坨一坨的,因而回头来学习怎么面向对象。那些不熟练的基础,总要还债的。php
出来混老是要还的java
SOLID 是Michael Feathers推荐的便于记忆的首字母简写,它表明了Robert Martin命名的最重要的五个面对对象编码设计原则数据库
S: 单一职责原则 (SRP) Single Responsibility Principle
O: 开闭原则 (OCP) Open/Closed Principle
L: 里氏替换原则 (LSP) Liskov Substitution Principle
I: 接口隔离原则 (ISP) Interface Segregation Principle
D: 依赖反转原则 (DIP) Dependency Inversion Principleide
当须要修改某个类的时候缘由有且只有一个(THERE SHOULD NEVER BE MORE THAN ONE REASON FOR A CLASS TO CHANGE)。换句话说就是让一个类只作一种类型责任,当这个类须要承当其余类型的责任的时候,就须要分解这个类。函数
举例子:不要让一个类,负责发邮件,还负责修改客户名称
不良代码:学习
class Email{
public function sendemail(){
//...
}
public function changeUsername(){
//...
}
}
改进版:this
class Email{
public function sendemail(){
//...
}
}
class User{
public function changeUsername(){
//...
}
}编码
软件实体应该是可扩展,而不可修改的。也就是说,对”扩展是开放的,而对修改是封闭的”。这个原则是在说明应该容许用户在不改变已有代码的状况下增长新的功能。url
实现开闭原则的关键就在于“抽象”。把系统的全部可能的行为抽象成一个抽象底层,这个抽象底层规定出全部的具体实现必须提供的方法的特征。做为系统设计的抽象层,要预见全部可能的扩展,从而使得在任何扩展状况下,系统的抽象底层不需修改;同时,因为能够从抽象底层导出一个或多个新的具体实现,能够改变系统的行为,所以系统设计对扩展是开放的。设计
看例子
<?php
abstract class vehicle
{
protected $name;
public function construct(){
}
public function getName()
{
return $this->name;
}
}
class car extends vehicle
{
public function construct()
{
parent::construct();
$this->name = 'car';
}
}
class bike extends vehicle
{
public function construct()
{
parent::construct();
$this->name = 'bike';
}
}
class task
{
private $vehicle;
public function construct($vehicle)
{
$this->vehicle = $vehicle;
}
public function dotask($kilometer)
{
$vName = $this->vehicle->getName();
if ($vName === 'car') {
return $this->CarRun($kilometer);
} elseif ($vName === 'bike') {
return $this->BikeRun($kilometer);
}
}
private function CarRun($kilometer)
{
echo '小汽车跑了'.$kilometer.'千米<hr>';
}
private function BikeRun($kilometer)
{
echo '自行车跑了'.$kilometer.'千米<hr>';
}
}
echo '<meta charset="utf-8">';
$car = new car;
$bike = new bike;
$task = new task($car);
$task->dotask(10);
$task = new task($bike);
$task->dotask(10);
?>
这个例子,若是增长一个车,bus,那么就要改动task任务这个类的底层代码
改良版本
<?php
interface vehicle
{
public function run($url);
}
class car implements vehicle
{
public function run($kilometer)
{
echo '小汽车跑了'.$kilometer.'千米<hr>';
}
}
class bike implements vehicle
{
public function run($kilometer)
{
echo '自行车跑了'.$kilometer.'千米<hr>';
}
}
class task
{
private $vehicle;
public function __construct($vehicle)
{
$this->vehicle = $vehicle;
}
public function dotask($kilometer)
{
$this->vehicle->run($kilometer);
}
}
echo '<meta charset="utf-8">';
$car = new car;
$bike = new bike;
$task = new task($car);
$task->dotask(10);
$task = new task($bike);
$task->dotask(10);
?>
改良后的车辆,任意增长新车型,都不会改动task的内容
当一个子类的实例应该可以替换任何其超类的实例时,它们之间才具备is-A关系
能够理解为:只要有父类出现的地方,均可以使用子类来替代。并且不会出现任何错误或者异常。可是反过来却不行。子类出现的地方,不能使用父类来替代。
定义1:若是对每个类型为 T1的对象 o1,都有类型为 T2 的对象o2,使得以 T1定义的全部程序 P 在全部的对象 o1 都代换成 o2 时,程序 P 的行为没有发生变化,那么类型 T2 是类型 T1 的子类型。
理解为“只要有父类出现的地方,均可以使用子类来替代”
定义2:全部引用基类的地方必须能透明地使用其子类的对象。
问题由来:有一功能P1,由类A完成。现须要将功能P1进行扩展,扩展后的功能为P,其中P由原有功能P1与新功能P2组成。新功能P由类A的子类B来完成,则子类B在完成新功能P2的同时,有可能会致使原有功能P1发生故障。
解决方案:当使用继承时,遵循里氏替换原则。类B继承类A时,除添加新的方法完成新增功能P2外,尽可能不要重写父类A的方法,也尽可能不要重载父类A的方法。
不良的设计:违背了里氏替换原则
假设一个父亲厨师会个炒鸡蛋的手艺,辣椒炒鸡蛋,儿子应该会这个手艺,可是儿子不会用辣椒,只会用大葱,因此炒出来的鸡蛋不同了。
<?php
class father{
public function cookeEgg(){
echo '辣椒炒鸡蛋<br/>';
}
}
class son1 extends father{
public function cookeEgg(){
echo '大葱炒鸡蛋<br/>';
}
}
echo '<meta charset="utf-8">';
$f = new father;
$f->cookeEgg();
$s1 = new son1;
$s1->cookeEgg();
?>
辣椒炒鸡蛋
大葱炒鸡蛋
违背了原则1:只要有父类出现的地方,均可以使用子类来替代。
如今父亲会辣椒炒鸡蛋,换成儿子来炒,结果儿子由于不敢碰辣椒,炒成了大葱烧鸡蛋。 这个儿子就不是好儿子。咱们假设这个是大儿子。
改良版本:出来一个好的二儿子的样子
<?php
class father{
public function cookeEgg(){
echo '辣椒炒鸡蛋<br/>';
}
}
class son2 extends father{
public function cookeEggWithScallion(){
echo '大葱炒鸡蛋<br/>';
}
}
echo '<meta charset="utf-8">';
$f = new father;
$f->cookeEgg();
$s2 = new son2;
$s2->cookeEgg();
$s2->cookeEggWithScallion();
?>
辣椒炒鸡蛋
辣椒炒鸡蛋
大葱炒鸡蛋
这个儿子,复合了定义1,父亲出来的地方,儿子出来就能提到,父亲会辣椒炒鸡蛋,儿子也会,因此儿子自动继承,可是二儿子还会大葱炒鸡蛋,二儿子就会两个炒蛋了
按解决方案:不要重写父亲的方法
规则1: 不要重写父亲的方案
规则2: 全部孩子都会有父亲的技能(父亲能辣椒炒鸡蛋,儿子就会,父亲出现的地方,儿子就能够替代)
规则3: 孩子会额外的技能,本身单独再写函数。(子类出现的地方,父亲不必定能替代)
依赖反转原则
实际理解为:高级逻辑层代码,不要由于底层的模块代码改变而变更。
高层模块:业务逻辑层,好比群发邮件,我决定发给购买者和没有购买者,那么群发邮件这个send的工做就是业务逻辑层的高层模块,而决定哪些用户时购买者的底层数据库查询操做属于底层模块。
举例子:老张开宝马,这个动做,开车是业务逻辑层,宝马车跑动是底层。
不良设计
<?php
class Bwm{
public function run(){
echo "开动宝马汽车";
}
}
class Audi{
public function run(){
echo "开动奥迪汽车";
}
}
//高层模块Driver 依赖了底层模块Bwm , 出来模块C Audi,我就只好改Driver了。
class Driver{
public function drive(Bwm $car){
$car->run();
}
}
echo '<meta charset="utf-8">';
$bwm = new Bwm;
$zhang = new Driver();
$zhang->drive($bwm);
$audi = new Audi;
$zhang->drive($audi); //奥迪我无法开了。 此处代码会报错。
?>
有个办法就是业务层,再写一个函数funciton driveAudi() 是否是很熟悉,咱们由于要负责处理额外的状况,又写了个看起来很重复函数。
改良的版本,把车作成接口
宝马和奥迪都是来实现车的底层逻辑函数。这样再新车进入的时候,就不用改动逻辑层的代码了。
<?php
interface Car{
public function run();
}
class Bwm implements Car{
public function run(){
echo "开动宝马汽车";
}
}
class Audi implements Car{
public function run(){
echo "开动奥迪汽车";
}
}
class Driver{
public function drive(Car $car){
$car->run();
}
}
echo '<meta charset="utf-8">';
$bwm = new Bwm;
$zhang = new Driver();
$zhang->drive($bwm);
$audi = new Audi;
$zhang->drive($audi);
?>
如今老张能够开宝马也能够开奥迪,你拿个大众,我也照样开。
再次理解这句话:高级逻辑层代码,不要由于底层的模块代码改变而变更。
不能强迫用户去依赖那些他们不使用的接口。换句话说,使用多个专门的接口比使用单一的总接口总要好。
interface Employee
{
public function work();
public function eat();
}
class Human implements Employee
{
public function work()
{
// ....working
}
public function eat()
{
// ...... eating in lunch break
}
}
机器人雇员不能吃,可是被强迫必须实现吃的接口
class Robot implements Employee
{
public function work()
{
//.... working much more
}
public function eat()
{
//.... robot can't eat, but it must implement this method
}
}
改良版本
interface Workable
{
public function work();
}
interface Feedable
{
public function eat();
}
interface Employee extends Feedable, Workable
{
}
class Human implements Employee
{
public function work()
{
// ....working
}
public function eat()
{
//.... eating in lunch break
}
}
// robot can only work
class Robot implements Workable
{
public function work()
{
// ....working
}
}
对面向对象的领悟,有助于在大型代码量的工程里,实现有效分离函数,互不干扰,团队协做。