一、设计模式的概念
设计模式:针对特定的问题提出的一中解决方案,和平台、语言无关。
作用:
- 更好的理解面向对象的思想
- 使我们的代码更加的优雅
使我们的代码更加容易扩展和复用
面向对象的一些原则:
高内聚、低耦合
- 对扩展开放,对修改封闭(开闭原则)
- 单一职责(一个类的功能应该单一)
二、常见的设计模式
单例
单例就是一个类只实例化一个对象
写法:
- 私有的构造
- 私有的克隆
- 私有的静态属性
- 公有的静态方法
class Dog
{
//5 私有的对象成员
protected static $dog;
//1. 把构造函数私有化
protected function __construct()
{}
//2 把clone私有化
protected function __clone()
{}
//3 反序列化方法__wakeup私有化
protected function __wakeup()
{}
//4 自己定义公有的方法产生对象
public static function getInstance()
{
// 如果没有创建对象,self::$dog为空,则创建对象
if (self::$dog == null) {
self::$dog = new self();
}
return self::$dog;
}
}
$dog1 = Dog::getInstance(); //new Dog();
使用场景
应用只启动一次,连接数据库等耗费资源的类都可以考虑使用单例
简单工厂
把客户端代码和类隔离,统一了对象的创建,修改了类不会影响到客户端的代码
abstract class Calculator implements IOperating
{
public $num1;
public $num2;
public abstract function operate();
}
class Add extends Calculator
{
public function operate()
{
return $this->num1 + $this->num2;
}
}
//核心类
class SimpleFactory
{
public static function create($operator)
{
switch($operator)
{
case '+':
return new Add();
break;
case '-':
break;
}
}
}
// 客户端代码
//所有对象的创建,都通过工厂
$obj = SimpleFactory::create('+');
$obj->num1 = 10;
$obj->num2 = 20;
echo $obj->operate();
工厂方法
与简单工厂不同,工厂类不再创建对象,把实例化对象的任务交给了子类
优点:解决了工厂不容易扩展的缺陷
缺点:客户端的代码负载
abstract class Calculator
{
public $num1;
public $num2;
public abstract function operate();
}
class Add extends Calculator
{
public function operate()
{
return $this->num1 + $this->num2;
}
}
class Sub extends Calculator
{
public function operate()
{
return $this->num1 - $this->num2;
}
}
//改进
class SimpleFactory
{
public static function create($className)
{
return new $className();
}
}
//客户端代码
//所有对象的创建,都通过工厂
$addObj = AddFactory::create();
$addObj->operate();
SimpleFactory::create('Add');
观察者模式
- 一个对象观察另一个对象,当被观察对象的状态发生变化时,观察者对象可以收到通知做出相应的改变。观察者模式提供了解耦观察者和被观察者两个类之间的解耦。
- 适用于:一个对象发生改变时,通知另外依赖它的对象,依赖对象做相应的改变
interface AbstractObserver
{
public function upate($info=null);//通知的消息
}
class Student implements AbstractObserver
{
public function update($info = null)
{
echo "好好学习<br/>";
}
}
//抽象的被观察者
interface AbstractSubject
{
public function attach(AbstractObserver $obserer);
public function detach(AbstractObserver $obserer);
public function notify();
}
//被观察者
class Subject implements AbstractSubject
{
protected $obserers = [];
public function __construct()
{
}
//添加观察者
public function attach(AbstractObserver $obserer)
{
$this->obserers[] = $obserer;
}
//删除观察者
public function detach(AbstractObserver $obserer)
{
//返回键值
$key = array_search($obserer, $this->obserers);
if ($key !== false) {
unset($this->obserers[$key]);
}
}
//通知观察者
public function notify()
{
foreach ($this->obserers as $key => $value) {
$value->update();//通知
}
}
}
适配器模式
可以将一个系统的接口转换成客户希望的接口,使得原本不兼容系统可以一块工作
使用场景:
- 使用了一个接口经常会发生变化的第三方库
- 旧系统的接口和新系统的不兼容,但旧系统比较复杂,不容易修改
//服务器端提供json
//{'name':'tom','age':18,'sex':'男'} json
//['name'=>'tom','age'=>18,'sex'=>'男']
class Server
{
public function json()
{
return json_encode(['name'=>'tom','age'=>18,'sex'=>'男']);
}
}
//需要转换的接口方法
interface IAdaper
{
//转换方法
public function json2Aarray();
}
//适配器类
class Adapter implements IAdaper
{
//1 包含一个server对象
protected $server;
public function __construct($server)
{
$this->server = $server;
}
//2 实现接口方法
public function json2Aarray()
{
var_dump($this->server->json());
//将json串转换为数组,第二个参数必须为true
return json_decode($this->server->json(),true);
}
}
//客户端代码
class Client
{
public function test($data)
{
foreach ($data as $key => $value) {
var_dump($value);
}
}
}
//客户端代码
$server = new Server();
//转换器
$adapter = new Adapter($server);
//客户端
$client = new Client();
var_dump($adapter->json2Aarray());
$client->test($adapter->json2Aarray());
策略模式
在不同情况会使用不同算法,要解耦算法对对象的影响,可以使用策略模式
//策略模式
interface ISalary
{
public function computeSalary();//计算工资的方法
}
class Manager implements ISalary
{
public function computeSalary()
{
echo "基本工资+补贴+效益<br/>";
}
}
class Teacher implements ISalary
{
public function computeSalary()
{
echo "基本工资+补贴<br/>";
}
}
//环境类
class Context
{
protected $strategy;//策略
//1 设置策略
public function setStrategy(ISalary $strategy)
{
$this->strategy = $strategy;
}
//调用对象实现策略
public function compute()
{
$this->strategy->computeSalary();
}
}
//客户端代码
$teacher = new Teacher();
$manager = new Manager();
$context = new Context();
$context->setStrategy($teacher);
$context->compute();
门面设计模式
使用场景:
- 给复杂的子系统提供一个简单的接口
提高子系统的独立性
优点:对客户屏蔽了子系统,因此减少了客户处理对象的数目,并且使得子系统使用起来更加方便
- 实现了子系统与客户之间的松耦合关系
//门面设计模式
class SystemA
{
public function demo1()
{
echo "A:demo1<br/>";
}
}
class SystemB
{
public function demo2()
{
echo "B:demo2<br/>";
}
}
class SystemC
{
public function demo3()
{
echo "C:demo3<br/>";
}
}
class Facade
{
protected $systemA;
protected $systemB;
protected $systemC;
public function __construct()
{
$this->systemA = new SystemA();
$this->systemB = new SystemB();
$this->systemC = new SystemC();
}
//新接口
public function demo()
{
$this->systemA->demo1();
$this->systemB->demo2();
$this->systemC->demo3();
}
}
依赖注入/控制反转(DI)
依赖注入(Dependency Injection)
控制反转(Inversion of Control)
当一个类的实例需要另一个类的实例协助时,在传统的程序设计过程中,通常由调用者来创建被调用者的实例。而采用依赖注入的方式,创建被调用者的工作不再由调用者来完成,因此叫控制反转,创建被调用者的实例的工作由IOC容器来完成,然后注入调用者,因此也称为依赖注入。
interface IRunning
{
public function run();//交通工具的运行方式
}
class Bus implements IRunning
{
public function run()
{
echo "坐公交";
}
}
class Ofo implements IRunning
{
public function run()
{
echo "骑自行车";
}
}
class Person
{
public $name;
public function __construct($name)
{
$this->name = $name;
}
public function goHome($tool)//person依赖于注入的对象
{
echo $this->name;
$tool->run();
echo "回家<br/>";
}
}
$wzy = new Person('Tom');
$wzy->goHome(new Bus());