建造者模式

建造者模式描述的其实是一个建造的过程对client透明,将一套建造流程封装起来,可以复用。比如建别墅的流程方法,用什么砖、什么料、怎么建造client不管,直接叫一批人搞定了。再比如说RPG游戏角色的创建,头发眼睛都是必须的,如何创建也是知道的,什么颜色的头发,什么样的眼睛都是和角色挂钩的,角色是什么client来定的。

主要角色
抽象建造者:定义一个接口,规范产品组成成分以及建造流程的实现,还要有一个返回结果的方法,最终是什么样,要知道。
具体建造者:实现抽象建造者定义的接口。
产品角色:建造者要用的产品,建造者用来建造最终的完整产品。
导演者角色:主要是调用具体建造者来建造最终完整复制的产品,也是为了隔离client和具体建造者。
上图
模式结构图
直接看代码


interface IBuilder{
    public function buildType();
    public function buildColor();
    public function getHouse();
}

class BigBuilder implements IBuilder{
    private $house;
    public function __construct(){
        $this->house=new House();
    }
    public function buildType(){
        $this->house->setType('big');
    }
    public function buildColor(){
        $this->house->setColor('white');
    }
    public function getHouse(){
        return $this->house;
    }
}
    
  

原型模式

原型模式就是copy模式,通过自身的clone复制一份对象。和new不一样的是,可以动态复制,new只能是初始化对象,而原型模式可以在对象处于任何状态的时候复制。至于说参数太多,原型模式可以使client不关心创建过程,那工厂模式完全也可以搞定。

上图
模式结构图
代码见真知


interface IPrototype{
    public function copy();
}

class Employee implements IPrototype{
    private $name;
    private $salary;
    public function __construct($name,$salary){
        $this->name=$name;
        $this->salary=$salary;
    }
    public function getName(){
        return $this->name;
    }
    public function getSalary(){
        return $this->salary;
    }
    public function setSalary($salary){
        $this->salary=$salary;
    }
    public function copy(){
        //return clone $this; 浅拷贝
        $obj=serialize($this);
        return unserialize($obj); //深拷贝
    }
    public function display(){
        echo "$this->name has $this->salary\n";
    }
}
$liangbopirates=new Employee('liangbopirates',1000000);
$copy=$liangbopirates->copy();
$liangbopirates->display();
$copy->display();
$liangbopirates->setSalary(800);
$liangbopirates->display();
$copy->display();

简述代理

代理基本上是指我们想要得到一个东西,但是又不是厂家直销,我们需要通过代理商才能拿到。代理分为正向代理和反向代理以及透明代理,待我一一道来~

正向代理:一般没有特殊说明,代理就是指正向代理。你想买个港版的iphone6,但是不能直接去香港买,可以有人能去香港买,你就取找这个人,这个人就是代购,你告诉代购你要什么样的,给代购钱,代购给你买,再拿给你。用在互联网上就是指client要访问目标server,需要通过代理服务器X去访问目标server,代理服务器X转发client的请求,并且返回给client目标server返回的数据。
为什么要用代理呢?有很多理由:

  1. 我们访问Google,被墙了,所以我们要用代理服务器去转发我们的请求访问国外的Google的服务器。
  2. 可以加速访问server,电信联通太慢,我们弄了一个加速代理,就像你说自己去香港买个iphone6太慢一样,让代购去买,2天就回来了。
  3. 有缓存作用,有很多client访问代理服务器X,X把request和response保存了,这样肯定会命中很多cache。
  4. 可以做权限控制,client要想访问目标服务器,要先看你够不够资格,中间加个正向代理就搞定了。比如现在很多公司就很坑爹,直接不让访问外网,怎么做到的,就是通过这个代理来做权限控制的。
  5. 隐藏自己的访问痕迹,连上代理服务器X,真正的server根本不知道是哪个client访问的。

反向代理:反向代理就是你感知不到这个代理,你以为你访问的是真正的server,其实你访问的是代理服务器X。就像你在taobao买个东西,说是厂家直销,怎么的你就信了,其实他是从阿里巴巴网站进货然后给你的。你以为自己买的厂家,其实买的是代理商的东西。
why:

  1. 保护目标服务器,不能把目标服务器直接暴露到client面前,要把目标服务器放到防火墙内部,在外面留一个代理分发器X,目标服务器只允许代理X访问,就算是X挂了,也不回影响防火墙内部的server。
  2. 可以做负载均衡,就是类似一个前端分发器,把请求分发到后端多个server。

透明代理是什么?就是你感知不到的代理,就是类似公司一个行为管理器。管理你的每一个操作,cd还是rm都会一清二楚,包括你下载什么片儿。

单例模式

单例模式基本是最简单的设计模式了,就是只有一个实例的模式,或者说一个id对应一个实例,什么时候需要用单例呢?就是你需要用一个实例,而且只用一个实例的时候了,哈哈。数据库访问重用同一个长连接,同一个终端只能有一个连接,这个时候用单例就是最好的办法。

单例不要用全局变量,污染名称空间不说,而且是可变的,不安全的,可以通过全局变量获取多个实例,所以要用static函数去唯一获取一个实例。
优点:可以节省资源,可以唯一获取一个实例,也可以允许多个可变的实例(id对应的实例)
缺点:很难扩展,单例压力太大,一个instance干了所有的事儿。
代码见真知



//单例模式,同一个uid只有一个instance
class Singleton{
private static $instance=array(); //保存所有的单个instance
private $uid;
private function _construct($uid){
$this->uid=$uid;
}
public function test(){
echo “I’am $this->uid\n”;
}
public static function getInstance($uid){
if(!self::$instance || is_null(self::$instance[$uid])){
self::$instance[$uid]=new self($uid);
}
return self::$instance[$uid];
}
//防止对象clone一个完全独立的intacne 赋值是reference无所谓
private function _clone(){
die(‘how dare u’);
}
}
$singleton1=Singleton::getInstance(1);
$singleton2=Singleton::getInstance(2);
$singleton1->test();
$singleton2->test();


php中的clone魔法函数,可以复制对象,不过也只是浅复制,如果要实现深复制就需要重载__clone函数。
php中static变量和方法是类的属性,可直接类名或者self访问,不可用$this访问,本例中construct函数是private,所以只能从内部初始化,而且此时没有对象生成,是不能直接用$this访问变量的,只能用self访问,self可访问的只有static(static函数只能访问static变量)。

策略模式

策略模式?说到策略那就是有很多了,36计啊,上学的时候有多种解题方法啊,买东西可以去很多超市啊,旅游可以去很多地方,可以有很多种出行方式。这个选择对我们来说是不透明的,我们可以去选择。对比之前的工厂模式,工厂是封闭的,要干啥你说,他来帮你干,策略模式是你要干啥都能干,但是要自己动手干,你要知道怎么干。而且策略模式可以知道是谁要干,根据是谁来做进一步的操作,最起码知道用户信息打印个log。

主要角色:

抽象策略:定义所有策略算法的公共接口
具体策略:实现抽象策略的接口,所有策略都是平等的。
环境角色:夹在client和具体策略算法直接的角色,来协调和分离client和策略。
优点:统一接口,统一算法策略。更好的扩展策略,符合开闭原则。避免复杂逻辑比如if-else。
缺点:client必须要知道所有的策略,才能选择用哪个。具体策略要想知道谁用他,就要传递通信,这个可以会增加开销,因为不是所有的策略算法都需要知道谁在用他,不需要传递参数。同时策略模式会增加很多策略类,这个可以结合其他设计模式来搞。
上图
模式结构图
代码见真知


<?php
interface IStrategy{
public function getPrice($goods);
}
class SilverVipCustomer implements IStrategy{
public function getPrice($goods){
echo “dear $goods->userName, 该商品打7折.\n”;
return $goods->price0.7;
}
}
class CopperVipCustomer implements IStrategy{
public function getPrice($goods){
echo “hi $goods->userName, 该商品打8折.\n”;
return $goods->price
0.8;
}
}
class NormalCustomer implements IStrategy{
public function getPrice($goods){
echo “sorry,$goods->userName, 该商品不打折.\n”;
return $goods->price;
}
}

适配器模式

适配器一般是用来适配接口的,如果Client调用的接口API发生了变化,客户端要怎么办,只能重新发版了。如果想让Client不必发版就可以使用新接口,那就需要使用适配器。不仅仅如此,适配器模式还可以使多个不能在一起工作的类变的能一起工作。注意,我说的是Client一开始调用API就使用适配器模式,而不是说要等API变化了之后再使用适配器模式。。

主要角色
目标接口:这个是客户端所需要的接口。
需要适配的对象:一般是修改逻辑导致接口变更。
适配器对象:适配器核心,把原来的接口适配包装成目标接口给客户端用。
上图
模式结构图
直接看代码


interface ITarget{
public function getDiscount();
}
class Adaptee{
public function getDiscountByVip($vip=0){
if(!$vip){
echo “9.9 dis\n”;
}
}
}
class Adapte implements ITarget {
private $adaptee;
public function __construct($adaptee){
$this->adaptee=$adaptee;
}
public function getDiscount(){
$vip=0;
$this->adaptee->getDiscountByVip($vip);
}
}
$adaptee=new Adaptee();
$adapte=new Adapte($adaptee);
$adapte->getDiscount();
其实适配器模式有2种,一种是刚才说的,叫对象适配器,是通过组合对象来适配客户端。还有一种是通过继承来适配的,不过只能适配某一个子类,而且php还不支持多重继承,好处是不需要额外变量保存adaptee对象,不赘述了。

组合模式

组合模式是干啥的?就是可以统一访问单个对象和组合对象的设计模式。比如菜单、文件夹,都是层级,我们访问一个文件和文件夹的方式是一样的。如果不用组合模式,那操作组合对象和单个对象使用不一样的接口,导致逻辑非常复杂。模型类似一个树,有树枝,有树叶。组合模式可以使Client对单个对象和组合对象的使用具有一致性,可以表示“部分-整体”结构。

主要角色

抽象组件:定义一个抽象接口,给组合对象和单个对象的操作提供统一的接口,可以使客户端统一访问。
组合对象:可以存储单个对象。
单个对象:没有子对象。
客户端:可以通过抽象组件接口透明操作组合或者单个对象。
上图
模式结构图
书签管理器


interface IMarkComponent{
    public function add($component);
    public function remove($component);
    public function getName();
    public function getUrl();
    public function display();
}
    
  

工厂模式(all-factory)

why?为什么要用工厂模式,创建对象的时候我们一般是直接new一个,如果有多个相关对象,难道我们要一new到底么?这个时候就需要工厂模式来把new对象的操作封装起来,直接用工厂生产对象,这样即便是后续做逻辑改动,也不会受到影响。

主要角色:抽象工厂-抽象产品-具体工厂-具体产品

先来看下简单工厂模式,简单工厂模式是工厂方法模式的简化版,我们一步一步来看。
如果需要买手机,买一个5s,买一个6,这样可以直接Factory搞定的,把买的操作封装到工厂里面。

直接看代码


abstract class Iphone{
public function _construct(){
}
}
class Iphone5s extends Iphone{
public function _construct(){
echo “I’am 5s\n”;
}
}
class Iphone6 extends Iphone{
public function
_construct(){
echo “I’am 6\n”;
}
}

php反射和__call

反射机制:它是指在PHP运行状态中,扩展分析PHP程序,导出或提取出关于类、方法、属性、参数等的详细信息,甚至包括注释。

PHP有很多这种动态获取的信息以及动态调用对象的方法的反射API。
本文主要说一下ReflectionClass这个类,该类有很多有意思的方法,说实话,用到的不是很多,getProperties获取属性,getFileName获取文件路径,getmethod获取方法。
下面代码说明ReflectionClass类的简单使用。


class Person{
    public  $name;
    private $sex;
    public function __construct($a,$b,$c){
        echo "$a\t$b\t$c\n";
    }
    public function construct($a,$b,$c){
        echo "$a\t$b\t$c\n";
    }
    public function  profile($name){
        echo "I'am $name\n";
    }
}
$person=new Person(1,2,3);
$args=array(1,2,3);
$class=new ReflectionClass('Person');
$fileName=$class->getFileName();
echo $fileName."\n";
$pro=$class->getProperties();
var_dump($pro);
$fun=$class->getmethod('profile');
$fun->invoke($person,'liangbopirates');
这些咋看起来不是那么有意义,我用他来干啥呢?