PHP面试高薪宝典系列: 几种常见的设计模式(附完整代码)

当简历上不经意间写上了精通OOP搞对象这一大招时候,面试官也肯定也会毫不留情的开启八股文模式,OOP三大特性是啥理解吗,搞对象的几个原则需要注意什么,IOC原理清楚吗?

此时面对面试官这个能唬住就能拿到50k的对象,自然也可以自信满满的开启装逼模式背起了他喜欢听的八股文,面对对象编程的特性有封装,继承,多态。遵循的基本原则有单一职责原则,里氏替换原则,依赖倒置原则,接口隔离原则。反正整体思想就是高内聚低耦合,只和一个妹纸处对象,爱意满满,和其他女生少点瓜葛。

这时候面试官想必觉得你很理解怎么处对象,把OOP理解的如此透彻,但是总觉得少了点什么,搞对象不能停留在嘴上面吧,还是实际的撸码。于是乎,他让你写出自己知道的搞对象模式和套路。

白慌,代码记得牢撸起来也是小问题。淡定的告诉面试官设计模式是日常编程搞对象的时候大佬总结出来的一些套路,按照这些套路更容易让对象(代码)搞得舒服,这些设计模式套路大概也就23种左右,工作中比较常用的也就那么几种, 我写下你看看这套路能不能成。

单例模式

单例模式,这种套路是最常见的,人如其名,就是一心一意的处一个对象,不管别人怎么干扰就是只有这一个对象。很明显,优点就是爱意单一,花销(内存)低不好地方就是限制了自由,难以扩展,通常这种巨大的单一对象多种爱意的表达方式有违背单一职责

要实现这种无论怎么操作都是同一个对象,首先肯定不能直接使用new的方式,因为new一个就多一个对象。要把构造函数私有化,其次既然不能new那对象哪里来,想必就要使用静态方法获取实例对象,在类的内部进行这个new操作,当然内部操作new出来的实例需要先保存在类私有静态变量才能通过类的静态方法获取内部的实例。也就是常说的三公一私。

class Singleton {
  private static $_instance; //保存实例
  private function __construct(){} //禁止直接外部实例化
  private function __clone(){} //禁止被clone
  public static function getInstance(){ 
  //获取保存在私有静态变量里的内部实例
      if (!self::$_instance) {
          self::$_instance = new self();
      }
      return self::$_instance;
  }
}

一般单例模式经常使用于配置文件的加载,以及数据库的连接实例,可以减少连接数,以及代码运行过程中占用的内存。

观察者

观察者模式,吃瓜群众观察者首先对自己感兴趣的事件做个订阅注册,比如陈老师事件,不用一直心心念的去想着,只要加个订阅对这种事件吃瓜开车的群,等有了陈老师事件的风吹草动,就自动地进行自己预定的吃瓜讨论逻辑

/****
 * 事件类
 */
class Event{
   private $_observers = [];
   function addObserver(Observer $observer)
  {  
         $this->_observers[] = $observer;
  }
 function notify() {
      foreach ($this->_observers as $observer) {
            $observer->update();
      }
  }
}

//观察者接口
interface Observer{
  function update();
}

//观察者类
class Observer1 implements Observer {
  function update() {
      echo "oberver1 running" . PHP_EOL;
  }
}
class Observer2 implements Observer {
  function update() {
  echo "oberver2 running" . PHP_EOL;
  }
}
$event = new Event();
$event->addObserver(new Observer1()); //为事件增加观察者
$event->addObserver(new Observer2());
$event->notify();  //通知观察者进行更新动作

观察者模式中观察者只需要订阅吃瓜消息, 具体如何吃瓜(收到事件如何处理)可以单独的去做,这就很好的实现了异步解耦的效果,在laravel框架中事件处理的方式使用的就是观察者模式。

简单工厂

简单工厂使用单一工厂实例的静态方法直接返回产品实例,简单粗暴

//简单工厂
interface People{
    function like();
}
class Man implements People
{
    function like()
    {
       echo "我喜欢电子产品" . PHP_EOL;
    }
}
class Women implements People
{
    public function like()
    {
        echo "我喜欢化妆品" . PHP_EOL;
    }
}
class Factory
{
    static function createMan()
    {
        return new Man();
    }
    static function createdWomen()
    {
        return new Women();
    }
}
$man = Factory::createMan();
$man->like();
$women = Factory::createdWomen();
$women->like();

工厂方法

对比简单工厂,单一工厂类抽象出多个工厂类。一个工厂类负责批量生产一个产品实例,增加一个产品需要增加一个工厂单独生产,不影响原有工厂类

//定义要生产类的接口
interface Product
{
    public function getName();
}
//实现生产类A
class ProductA implements Product
{
    public function getName()
    {
        return 'ProductA';
    }
}
//定义抽象工厂类
abstract class Factory
{
    abstract public function createProduct();
}
//实现生产产品A的工厂类方法
class FactoryA extends Factory
{
    public function createProduct()
    {
        return new ProductA();
    }
}
$factoryA = new FactoryA();
$productA = $factoryA->createProduct();
echo $productA->getName();

抽象工厂

进一步抽象工厂类,使一个工厂类具有分组流水线功能。生产多个零件组装成一个成品,从而减少工厂类。

interface Mouse{     //鼠标产品接口(核心类)
      public function getMouse();  //获取鼠标
  }
class DellMouse implements Mouse   //戴尔类产品
  {
      public function getMouse(){
          echo "我是戴尔鼠标<br>";
      }
  }
class LenovoMouse implements Mouse   //联想类产品
  {
      public function getMouse(){
          echo "我是联想鼠标<br>";
      }
  }
interface Keybo{     //键盘产品接口(核心类)
      public function getKeybo();  //获取键盘
  }
class DellKeybo implements Keybo   //戴尔类产品
  {
      public function getKeybo(){
          echo "我是戴尔键盘<br>";
      }
  }
class LenovoKeybo implements Keybo   //联想类产品
  {
      public function getKeybo(){
          echo "我是联想键盘<br>";
      }
  }

 abstract class Factory{      //抽象工厂类(核心工厂类)
      abstract static function createMouse(); //生产鼠标
      abstract static function createKeybo();//生产键盘
  }
class DellFactory extends Factory   //戴尔牌工厂继承工厂类
  {
      public static function createMouse()
      {
          return new DellMouse();   //工厂创建戴尔鼠标类对象
      }
      public static function createKeybo()
      {
          return new DellKeybo();   //工厂创建戴尔键盘类对象
      }
  }
class LenovoFactory extends Factory   //联想牌工厂继承工厂类
  {
      public static function createMouse()
      {
          return new LenovoMouse();   //工厂创建联想鼠标类对象
      }
      public static function createKeybo()
      {
          return new LenovoKeybo();   //工厂创建联想键盘类对象
      }
  }

 $dell_keybo  =  DellFactory::createKeybo(); 
 echo  $dell_keybo->getKeybo();  //输出“我是联想鼠标” 
 $lenovo_mouse  =  LenovoFactory::createMouse();  
 echo  $lenovo_mouse->getMouse(); //输出“我是联想键盘”

适配器模式

通过适配器类把本身不能直接使用、要适配的类进行一个封装适配成目标抽象类,便于直接调用。
比如laravel中的队列驱动可以实现redis队列,同步队列,mysql队列任意切换。本身就是规定了要实现适配的队列基本操作方法,至于各种驱动方式下不能进行直接调用的操作方法要进行适配完成

//目标类
interface Target
{
    public function request();
}
//要进行适配的类
class Adaptee
{
    public function specificRequest()
    {
        echo '特殊请求!' . PHP_EOL;
  }
}
//适配器
class Adapter implements Target
{
    private $adaptee;

    public function  __construct(Adaptee  $adaptee)
    {
        $this->adaptee = $adaptee;
    }
    public function request()
    {
        $this->adaptee->specificRequest();
        // TODO: Implement request() method.
    }
}
$adapter  = new Adapter(new Adaptee());
$adapter->request();

策略模式

统筹大局的策略调用者能修改接收不同的策略处理逻辑,最终得到不同的处理结果。

/****
 * 策略模式
 */
interface Strategy{
  public function doLogic($data);
}
class StrategyA implements Strategy {
  public function doLogic($data){
 sort($data);
 return $data;
  }
}
class StrategyB implements Strategy {
  public function doLogic($data){
 ksort($data);
 return $data;
  }
}

class Context{
  private $strategy = null;
 function __construct(Strategy $strategy){
  $this->strategy = $strategy;
  }
  function doLogic ($data) {
  return $this->strategy->doLogic($data);
  }
}

$res = (new Context(new StrategyB()))->doLogic(["a" =>1 , "b"]);

职责链

职责链模式简单可以理解为在一个组织架构中,每个人扮演的角色不同,被赋予的职责不同。例如现实生活中,作为小小的一个撸码程序猿,一般组织架构中都会有自己的直属leader,然后有技术部CTO,以及老板,每当我们请假的时候都需要提请假审批,请一天需要直属领导,如果是请3天或者更久需要CTO,CEO来一个个审批,挨个临幸,最终形成了一条权利责任链。

//职责角色抽象类
abstract class Handler
{
    protected $leader;

    public function setLeader($leader) {
        $this->leader = $leader;
    }

    abstract public function handleRequest($request);
}
//直属领导批三天的假
class LeaderHandler extends Handler
{
    public function  handleRequest($request)
    {
        if ($request == '请加3天') {
            echo "直属领导批示请假成功" . PHP_EOL;
        } elseif ($this->leader){
            $this->leader->handleRequest($request);
        }
        // TODO: Implement handleRequest() method.
    }
}
//老板批5天的假
class BossHandler extends Handler
{
    public function  handleRequest($request)
    {
        if ($request == '请加5天') {
            echo "老板批示请假成功" . PHP_EOL;
        } elseif ($this->leader){
            $this->leader->handleRequest($request);
        }
        // TODO: Implement handleRequest() method.
    }
}
$request = '请加5天';
$leader = new LeaderHandler();
$leader->setLeader(new BossHandler());
$leader->handleRequest($request);
本作品采用《CC 协议》,转载必须注明作者和本文链接
PHP是世界上最好的编程语言,它能快速的进行技术变现,让代码多一份价值。
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 2

完整版php设计模式:github.com/734380794/design-patter...

1年前 评论
PHP之父一只码 (楼主) 1年前

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!
UFO @ 一只码科技
文章
9
粉丝
36
喜欢
77
收藏
176
排名:508
访问:1.6 万
私信
所有博文
社区赞助商