PHP创建对象的几种设计模式

1.单例模式

单例模式.png
class Preferences{

    private $props = array();

    private static $instance;
    //设置为私有不能在外部初始化
    private function __construct()
    {
    }
    public function getProperty($key) {
        return $this->props[$key];
    }
    public function setProperty($key,$val){
        $this->props[$key] = $val;
    }

    public static function getInstance()
    {
//        if (empty(static::$instance)){
//            static::$instance = new Preferences();
//        }
//        return static::$instance;
        //这样写代码会进行类型提示,编译器会自动推断类型
        if (!self::$instance instanceof self) {
            self::$instance = new self();
        }
        return self::$instance;
    }

    //防止clone,设置为私有
    private function __clone()
    {

    }
}

$instance = Preferences::getInstance();

$instance->setProperty('a','1');
$instance->setProperty('b','2');

unset($instance);//下面的值依旧可以用
$instance2 = Preferences::getInstance();
echo $instance2->getProperty('a');
echo $instance2->getProperty('b');

单例模式保证任何时刻都只有一份实例,可以用来对全局设置一些配置,也可以在需要用到全局变量的地方写到单例里面,方便管理。

2.工厂方法模式

一个关于个人事务管理的项目,要求管理预约对象,业务团队和另一个公司建立关系,需要用一个叫做BloggsCal的格式和他们团队交流预约的相关数据,以后还可能有其他的数据格式。

image.png
abstract class ApptEncode
{
    abstract function encode();
}

//Bloggs编码格式
class BloggsApptEncoder extends ApptEncode
{
    function encode()
    {
        echo ' appointment -- bloggs';
    }
}
//Mega编码格式
class MegaApptEncoder extends ApptEncode
{
    function encode()
    {
        echo ' appointment -- Mega';
    }
}

//根据不同的场景获取编码器类,调用相关的编码器生成数据,可以根据传递的参数类型生成
class CommsManager
{
    const BLLOGS = 1;
    const MEGA =2;
    private $mode;

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

    public function getEncoder()
    {
        switch ($this->mode) {
            case (self::BLLOGS):
                return new BloggsApptEncoder();
            default:
                return new MegaApptEncoder();
        }
    }
    //页眉输出
    public function getHeads() {
        switch ($this->mode) {
            case (self::BLLOGS):
                return "bllggs header";
            default:
                return "mega header";
        }
    }
}

通过标志位加switch判断可以在getEncoder方法里面实例化需要的对象,但是如果需要额外的操作,比如增加页眉,switch语句会遍布整个类,多态的使用可以避免if或者switch判断。
把管理类改成下面的形式就是工厂方法模式,工厂方法模式会导致特殊代码重复,不能为创建者创建子类就只是为了实现工厂方法模式,如果没有页眉也页脚这样的话,完全可以用改进前的形式。

工厂方法模式把创建者类与要生产的产品类分离开来,创建者是一个工厂类,其中定义了用于生产产品对象的类方法。如果没有提供默认实现就要创建者类的子类来实例化,一般情况下一个创建者的子类对应一个产品。

abstract class CommsManager
{
   abstract public function getHead();
   abstract public function getFooter();
   abstract public function getEncoder();
}

class BloggsCommsManager extends CommsManager
{
    public function getEncoder()
    {
       return new BloggsApptEncoder();
    }
    public function getFooter()
    {
       return "Bloggs -- footer ";
    }

    public function getHead()
    {
        return "Bloggs -- header ";
    }
}

class MegaCommsManager extends CommsManager
{
    public function getEncoder()
    {
        return new MegaApptEncoder();
    }
    public function getFooter()
    {
        return "mega -- footer ";
    }

    public function getHead()
    {
        return "mega -- header ";
    }
}
image.png

3.抽象工厂模式

如果上面增加了联系人和代办事项两个类别,要求工厂要求创建一系列的类,可以在抽象类的接口声明相关方法,也可以根据传入的参数判断。

抽象工厂模式.png
abstract class ApptEncode
{
    abstract function encode();
}
abstract class TtdEncode
{
    abstract function encode();
}
abstract class ContactEncode
{
    abstract function encode();
}

//Bloggs编码格式
class BloggsApptEncoder extends ApptEncode
{
    function encode()
    {
        echo ' appointment -- bloggs';
    }
}
class BloggsTtdEncoder extends ApptEncode
{
    function encode()
    {
        echo ' ttd -- bloggs';
    }
}
class BloggsContactEncoder extends ApptEncode
{
    function encode()
    {
        echo 'contact -- bloggs';
    }
}
//Mega编码格式
class MegaApptEncoder extends ApptEncode
{
    function encode()
    {
        echo ' appointment -- Mega';
    }
}


//工厂方法模式把创建者类与要生产的产品类分离开来,创建者是一个工厂类,其中定义了用于生产产品对象的类方法。如果没有提供默认实现就要创建者类的子类来实例化
//一般情况下一个创建者的子类对应一个产品。

abstract class CommsManager
{
   abstract public function getHead();
   abstract public function getFooter();
   //创建一系列的对象,不是一个
   abstract public function getApptEncoder();
   abstract public function getThdEncoder();
   abstract public function ContactEncoder();
}
//Mega和这个相似
class BloggsCommsManager extends CommsManager
{
    public function getApptEncoder()
    {
       return new BloggsApptEncoder();
    }
    public function getThdEncoder()
    {
        return new BloggsTtdEncoder();//
    }
    public function ContactEncoder()
    {
        return new BloggsContactEncoder();
    }
    public function getFooter()
    {
       return "Bloggs -- footer ";
    }

    public function getHead()
    {
        return "Bloggs -- header ";
    }
}

4.原型模式

问题.png

可以用抽象工厂模式实现,如下


image.png

但是会产生巨大的平行继承体系,而且不那么灵活。可以原型模式。

class Resource{};
class FishResource extends Resource{};

class Sea{
    public $resource;
    public function __construct(Resource $resource)
    {
        $this->resource = $resource;
    }

    public function __clone()
    {
        // TODO: Implement __clone() method.
        $this->resource = clone $this->resource;
    }
}
class ErathSea extends Sea {}
class MarsSea extends Sea{}

class Forest{}
class EarthForest extends Forest {}
class MarsForest extends Forest{}

class Plains {}
class EarthPlains extends Plains {}
class MarsPlains extends Plains{}

class TerrainFactory
{
    private $sea;
    private $forest;
    private $plains;
    function __construct(Sea $sea, Plains $plains,Forest $forest)
    {
        $this->sea = $sea;
        $this->forest = $forest;
        $this->plains = $plains;
    }

    public function getSea()
    {
        return clone $this->sea;
    }
    public function getForeset()
    {
        return clone $this->forest;
    }
    public function getPlains()
    {
        return clone $this->plains;
    }
}

//如果生成的对象是由其他的对象组成的,代码具有灵活性,假设sea对象都能包含Resource对象(FishResource ,OilResource。

设计模式经常相互包含,比如抽象工厂模式里面包含工厂方法模式。下面是一个单例模式和抽象工厂模式相结合而且常见的例子。

class config {
    static $config = ['codeType' => 'BloggsCommsManager'];
}

class Setting{

    private $props = array();

    private static $instance;
    //设置为私有不能在外部初始化
    private function __construct()
    {
    }
    public static function getInstance()
    {
        if (!self::$instance instanceof self) {
            self::$instance = new self();
        }
        return self::$instance;
    }
    //防止clone,设置为私有
    private function __clone()
    {

    }

    public function getEncoder()
    {
        //假设根据配配置来的
        $class = config::$config['codeType'];
        if (class_exists($class) && $class instanceof CommsManager) {
            return new $class();
        }else{
            return new BloggsCommsManager();
        }
    }
}

$commsManager = Setting::getInstance()->getEncoder();
$commsManager->getApptEncoder()->encode();
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,233评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,013评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,030评论 0 241
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,827评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,221评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,542评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,814评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,513评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,225评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,497评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,998评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,342评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,986评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,812评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,560评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,461评论 2 266

推荐阅读更多精彩内容

  • 设计模式汇总 一、基础知识 1. 设计模式概述 定义:设计模式(Design Pattern)是一套被反复使用、多...
    MinoyJet阅读 3,820评论 1 15
  • 设计模式基本原则 开放-封闭原则(OCP),是说软件实体(类、模块、函数等等)应该可以拓展,但是不可修改。开-闭原...
    西山薄凉阅读 3,663评论 3 13
  • 参考资料:菜鸟教程之设计模式 设计模式概述 设计模式(Design pattern)代表了最佳的实践,通常被有经验...
    Steven1997阅读 1,123评论 1 12
  • 无戒老师你好: 无戒老师我每天爱做的一件事,就是在简书里看文章,我非常喜欢看简书里《谈写作》的文章,因为我喜欢...
    贵人常伴阅读 259评论 3 2
  • 【科幻武侠】长篇《TH的人间冒险》目录 作者:tzykenshin 金黄的光线从薄如蝉翼的窗缝中洒进小楼,空气中的...
    tzykenshin阅读 303评论 0 0