这些原则,结合在一起能够方便程序员开发易于维护和扩展的软件,也让开发人员轻松避免代码异味,易于重构代码,也是敏捷或自适应软件开发的一部分。

五大原则(S.O.L.I.D)都代表什么?

* S - 单一职责原则
* O - 开发封闭原则
* L - 里氏替换原则
* I - 接口隔离原则
* D - 依赖倒置原则

单一职责原则

单一职责原则(简称:S.R.P)指出:一个类应该有且只有一个去改变它的理由,这意味着一个类应该只有一项工作,只完成一个功能。

例如,我们需要去计算一些图形的面积,申明图形类,并提供计算面积方法

<?php 
class Circle {
public $radius;

public function __construct($radius)
{
    $this->radius = $radius;
}

}

class Square {
		public $length;

		public function __construct($length)
		{
				$this->length = $length;
		}

}

然后,我们输出面积(我们申明一个输出类):

class AreaOutPut {

protected $shape;

public function __construct($shape)
{
    $this->shape = $shape;
}

public function caculator()
{
    //求面积的逻辑
}


public function outputResult()
{
    echo 'Area is :'.$this->caculator();
    echo "<br>";
}
}

我们计算图形面积,直接调用就可以了

$circle_area = new AreaOutPut(new Circle(2));
$circle_area->outputResult();

$square_area = new AreaOutPut(new Square(5));
$square_area->outputResult();

假如我们需要输出的是JSON数据或者其他形式的数据,那该怎么办?如果也写在AreaOutPut类里面,则违反了单一职责原则(SRP),AreaOutPut不仅仅需要计算和,还需要负责用户需要什么形式的数据.

为了解决这个问题,我们需要新建一个类,解决客户端需要何种形式数据的问题.

class OutPutArea {
protected $area;

public function __construct($area)
{
    $this->area = $area;
}

public function outputJSON()
{
    echo json_encode($this->area->caculator);
}

public function outputXML()
{
    //略
}
}

这样就可以可以获得我们需要的形式的数据了

$circle_area = new AreaOutPut(new Circle(2));
$circle_output = new OutPutArea($circle_area);
$circle_output->outputJSON();
$circle_output->outputXML();

开发封闭原则

开发封闭原则指的是:对象或实体应该对扩展开放,对修改封闭。也就是说一个类应该是可以扩展的,而不可修改的

我们看下上面的求面积类,计算方法,我们可以这么写

public function caculator()
{
    //求面积的逻辑
    if (is_a($this->shape,'Circle')) {
        return 3.14 * $this->shape->radius * $this->shape->radius;
    } elseif (is_a($this->shape,'Square')) {
        return $this->shape->length * $this->shape->length;
    }
}

但是如果,我需要新增图形的种类,比如三角形,菱形等等,我们需要添加很多的if else,这就违背了开放封闭原则.

但是我们有一种更好的方式去实现,把计算面积从AreaCalculator中移除,移到图形类中去,像这样:

<?php 
class Circle {
		public $radius;

		public function __construct($radius)
		{
				$this->radius = $radius;
		}

		public function getArea()
		{
				return 3.14 * $this->shape->radius * $this->shape->radius;
		}

}

class Square {
		public $length;

		public function __construct($length)
		{
				$this->length = $length;
		}

		public function getArea()
		{
				return $this->shape->length * $this->shape->length;
		}

}

这样看上去是不是就比较科学了,但是我们还可以进一步改进,比如,我们需要保证所有的图形类都有getArea()方法,面对接口编程而不是面对实现编程,

<?php

interface Shape {
		public function getArea();
} 
class Circle implements Shape {
		public $radius;

		public function __construct($radius)
		{
				$this->radius = $radius;
		}

		public function getArea()
		{
				return 3.14 * $this->shape->radius * $this->shape->radius;
		}

}

class Square implements Shape {
		public $length;

		public function __construct($length)
		{
				$this->length = $length;
		}

		public function getArea()
		{
				return $this->shape->length * $this->shape->length;
		}

}

这样我们就保证了所有的图形类都有方法 getArea(),我们再看下AreaCalculator中的计算方法,就可以这么改进了

public function caculator()
{
    //求面积的逻辑
    if (is_a($this->shape,'ShapeInterface')) {
        return $this->shape->getArea();
    }
}

里氏替换原则

里氏替换原则指的是:如果对每一个类型为 X1的对象 y1,都有类型为 X2 的对象y2,使得以 X1定义的所有程序 P 在所有的对象 y1 都代换成y2 时,程序 P 的行为没有发生变化,那么类型 X2 是类型 X1 的子类型。也就是说所有引用基类的地方必须能透明地使用其子类的对象。

通俗的讲,里氏替换原则说的就是:子类可以扩展父类的功能,但不能改变父类原有的功能,包含4层含义:

* 子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法
* 子类中可以增加自己特有的方法
* 当子类的方法重载父类的方法时,方法的前置条件(即方法的形参)要比父类方法的输入参数更宽松
* 当子类的方法实现父类的抽象方法时,方法的后置条件(即方法的返回值)要比父类更严格

接口隔离原则

接口隔离原则指的是:客户端不应该依赖它不需要的接口;一个类对另一个类的依赖应该建立在最小的接口上

以鸟类为例子:定义一个接口类,鸟都有翅膀,都能飞

<?php

interface BirdInterface {

		public function hasWing();

		public function fly();
} 

麻雀,鸵鸟都实现接口:

class Sparrow implements BirdInterface {

public function hasWing()
{
    echo ' my has two wing';
}

public function fly()
{
    echo 'my is sparrow i can fly';
}
}


class Ostrich implements BirdInterface {

		public function hasWing()
		{
				echo ' my has two wing';
		}

		public function fly()
		{
				echo 'my is Ostrich i can\'t fly';
		}
}

但是鸵鸟并不会飞,它不需要这个接口.这就违反了接口隔离原则(ISP).

所以我们可以这样:把会飞的特性独立出来,新申明一个接口

<?php

interface BirdInterface {

		public function hasWing();

} 

interface FlyBirdInterface {
		public function fly();
}
class Sparrow implements BirdInterface,FlyBirdInterface {

		public function hasWing()
		{
				echo ' my has two wing';
		}

		public function fly()
		{
				echo 'my is sparrow i can fly';
		}
}


class Ostrich implements BirdInterface {

		public function hasWing()
		{
				echo ' my has two wing';
		}
}

这样就不会违反接口隔离原则了.

依赖倒置原则

依赖反转原则指的是:高层次的模块不应该依赖于低层次的模块,他们都应该依赖于抽象.抽象不应该依赖于具体实现,具体实现应该依赖于抽象.

例如:我们框架比较常见的模型中,定义的驱动类

<?php 
class Model {

		protected $driver;

		public function __construct(MysqlDriver $mysqldriver)
		{
				$this->driver = $mysqldriver;
		}
}

这边我们的Model类是高层次的模块,$mysqldriver是低层次的模块,但是这边是强依赖的,如果我数据库引擎换成了sqlite 则必须修改model 类,这就违反了开放封闭原则.

Model并不需要关心你使用的是什么引擎,为了解决这个问题,我们再一次使面向对接口编程,因为高层次和低层次模块应该依赖于抽象.

我们申明驱动接口:

<?php

interface DbInterface {

		public function dbConnect();
}

所有的数据库驱动必须实现驱动接口:

class MysqlDriver implements DbInterface {

public function dbConnect()
{
    //mysql的连接
}
} 

class SqliteDriver implements DbInterface {

		public function dbConnect()
		{
				//sqllite的连接
		}
}

我们再看下我们的Model类:

class Model {

protected $driver;

public function __construct(DbInterface $dbdriver)
{
    $this->driver = $mysqldriver;
}
}

这样,无论我们使用的是何种的数据库,只要是实现了接口DbInterface的,就不会影响到我们应用的运行.

S.O.L.I.D刚刚学可能会比较吃力,然后还不太讨好,但是只要坚持下去写,它就可以让你的代码很容易地扩展、修改、测试和重构,对于后期项目的维护和修改帮助是非常大的。