PHP设计模式(一)

设计模式六大原则

开放封闭原则:一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。
里氏替换原则:所有引用基类的地方必须能透明地使用其子类的对象.
依赖倒置原则:高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。
单一职责原则:不要存在多于一个导致类变更的原因。通俗的说,即一个类只负责一项职责。
接口隔离原则:客户端不应该依赖它不需要的接口;一个类对另一个类的依赖应该建立在最小的接口上。
迪米特法则 :一个对象应该对其他对象保持最少的了解。

单例模式 (创建设计模式)

要点:只有一个实例,作为对象的创建模式,单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。
常见: 数据库连接,日志错误记录(多种用途使用多种模式)

<?php

/**
 * 单例模式
 */
class Singleton
{
    /**
     * @var self[保存实例]
     */
    private static $instance;

    /**
     * @var
     */
    public $mix;

    /**
     * return self instance [创建一个用来实例化对象的方法]
     */
    public static function getInstace()
    {
        var_dump(isset(self::$instance));
        if (!self::$instance instanceof self) {
            self::$instance = new self();
        }
        return self::$instance;
    }

    /**
     * Singleton constructor.构造函数为private,防止创建对象
     */
    private function __construct()
    {
        echo "实例初始化了";
    }

    private function __clone()
    {
        // TODO: Implement __clone() method.
        trigger_error('Clone is not allowed!');
    }
}

// @Test
$firstSingle = Singleton::getInstace();
$secondSingle = Singleton::getInstace();

$firstSingle->mix = 'one';
//一开始mix是赋值‘one',所以打印出one
print_r($firstSingle->mix);

//由于getInstace该方法保证了Singleton类只能有一个实例,不会再重新new,minx依然用firstSingle的,mix被改变成’two‘
$secondSingle->mix = 'two';  
print_r($firstSingle->mix);
print_r($secondSingle->mix);

打印结果:bool(false) 实例初始化了bool(true) onetwotwo

简单工厂模式 (创建设计模式)

要点:可以根据参数的不同返回不同类的实例。简单工厂模式专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类。

<?php

/**
 * 简单工厂模式
 */

interface SystemFactory
{
    public function createSystem($type);
}

class MySystemFactory implements SystemFactory
{
    // 实现工厂方法
    public function createSystem($type)
    {
        switch ($type) {
            case 'Mac':
                return new MacSystem();
            case 'Win':
                return new WinSystem();
            case 'Linux':
                return new LinuxSystem();
        }
    }
}

class MacSystem
{
    public function __construct()
    {
        echo "I am Mac system</br>";
    }
}

class WinSystem
{
    public function __construct()
    {
        echo "I am Win system</br>";
    }
}

class LinuxSystem
{
    public function __construct()
    {
        echo "I am Linux system</br>";
    }
}

//创建系统工厂
$systemObj = new MySystemFactory();

$systemArray = array('Mac','Linux','Win');
foreach ($systemArray as $val) {
    $systemObj->createSystem($val);
}

打印结果:
I am Mac system
I am Linux system
I am Win system

工厂模式 (创建设计模式)

要点:此模式中,通过定义一个抽象的核心工厂类,并定义创建产品对象的接口,创建具体产品实例的工作延迟到其工厂子类去完成。这样做的好处是核心类只关注工厂类的接口定义,而具体的产品实例交给具体的工厂子类去创建。当系统需要新增一个产品,无需修改现有系统代码,只需要添加一个具体产品类和其对应的工厂子类,是系统的扩展性变得很好,符合面向对象编程的开闭原则;

我的理解:

按我平时喜欢玩的篮球游戏nba2k系列来代入。我来模拟整个NBA联盟,一开始是没有球员的,我需要不断的创建球员,具体就交给Nba底下的各个子类,也就是各个球队去创建就好

首先有一个球员接口,可以定义球员的投射还有抢篮板球的动作,注意这里的每个球员暂时只有这两个动作,生产出来的球员只有投射和抢篮板动作

interface Player{
    public function shot();
    public function rebound();
}

然后具体到球员自己的动作

class Wade implements Player
{
    public function shot()
    {
        echo "I can shot in middle distance<br>";
    }
    public function rebound()
    {
        echo "I can crash the boards <br>";
    }
}

class Kobe implements Player
{
    public function shot()
    {
        echo "I can shot the three-point <br>";
    }
    public function rebound()
    {
        echo "I can crash the boards <br>";
    }
}

定义一个抽象的核心工厂类,也就是整个游戏有一个创建球员页面

abstract class NbaFactory{
    abstract  static function createPlayer();
}

我喜欢热火,就来操作热火还有湖人好了,创一个韦德还有科比来玩玩

class HeatTeam extends NbaFactory{
    public static function createPlayer()
    {
        return new Wade();
    }
}
class LakersTeam extends  NbaFactory{
    public static function createPlayer()
    {
        return new Kobe();
    }
}

一切准备好了,就去场上试试能不能打:选人然后投篮抢篮板

$heatPlayer = HeatTeam::createPlayer();
$heatPlayer->shot();
$heatPlayer->rebound();

$lakersPlayer = LakersTeam::createPlayer();
$lakersPlayer->shot();
$lakersPlayer->rebound();

所以工厂模式:不需要NBA这个背后大boss亲自去创建球员,只需要定义一个想要创建球员这样的接口,实例工作就交给子类也就是球队去创建就行了,当我们想要增加一个自己喜欢的球员来玩,只需要添加一个具体的球员,然后设置他有什么动作呀,能力呀,再对应你想要他在哪一支队伍就ok了,这样就不需要修改现有系统代码,系统的扩展性变得很好,符合面向对象编程的开闭原则

整体代码如下:

<?php

/**
 *  球员动作
 * Interface Player
 */
interface Player{
    public function shot();
    public function rebound();
}

/**
 * 设置韦德投篮以及抢篮板
 * Class Wade
 */
class Wade implements Player
{
    public function shot()
    {
        echo "I can shot in middle distance<br>";
    }
    public function rebound()
    {
        echo "I can crash the boards <br>";
    }
}

/**
 * 设置科比投篮以及抢篮板
 * Class Kobe
 */
class Kobe implements Player
{
    public function shot()
    {
        echo "I can shot the three-point <br>";
    }
    public function rebound()
    {
        echo "I can crash the boards <br>";
    }
}

/**
 * 整个游戏NBA具备创建球员这一项功能
 * Class NbaFactory
 */
abstract class NbaFactory{
    abstract  static function createPlayer();
}

/**
 * 热火队创建了韦德这样的球员
 * Class HeatTeam
 */
class HeatTeam extends NbaFactory{
    public static function createPlayer()
    {
        return new Wade();
    }
}

/**
 * 湖人队创建了科比这样的球员
 * Class LakersTeam
 */
class LakersTeam extends  NbaFactory{
    public static function createPlayer()
    {
        return new Kobe();
    }
}

/**
 *  选择热火队,并且测试韦德的动作
 */
$heatPlayer = HeatTeam::createPlayer();
$heatPlayer->shot();
$heatPlayer->rebound();

/**
 *  选择湖人队,并且测试科比的动作
 */
$lakersPlayer = LakersTeam::createPlayer();
$lakersPlayer->shot();
$lakersPlayer->rebound();

抽象工厂模式 (创建设计模式)

要点:提供一个创建一系列相关或相互依赖对象的接口。注意:这里和工厂方法的区别是:一系列(多个),而工厂方法只有一个。。在工厂方法模式中,一个具体的工厂负责生产一类具体的产品,即一对一的关系,但是,如果需要一个具体的工厂生产多种产品对象,那么就需要用到抽象工厂模式了。

我的理解:
工厂模式,一个工厂辅助生产一类具体的产品,就像上边的例子,球员创建,只有投篮还有抢篮板动作,无论创建韦德科比还是詹姆斯都是只有这两种。而抽象工厂,需要生产多种产品,例如下面有两个游戏页面,球衣样式设置和球鞋样式设置,这两个页面底下是各种包含自己的方法,球衣页面是主客场球衣设置,球鞋页面是高低帮和颜色设置。

整体代码如下:

<?php

/**
 * 球衣样式设置(客场球衣&主场球衣)
 * Interface Uniform
 */
interface Uniform{
    public function home();
    public function away();
}

/**
 * 设置热火主客场球衣样式
 * Class HeatUniform
 */
class HeatUniform implements Uniform
{
    public function home()
    {
        echo "Set the Heat's uniform with red and white <br>";
    }

    public function away()
    {
        echo "Set the Heat's uniform with black and red <br><br>";
    }
}

/**
 *  球鞋样式设置(球鞋款式&球鞋主颜色)
 * Interface Shoes
 */
interface Shoes{
    public function style();
    public function color();
}

/**
 * 设置热火主客场球衣样式
 * Class HeatUniform
 */
class HeatShoes implements Shoes
{
    public function style()
    {
        echo "Set the Heat's Shoes'style as height shoes <br>";
    }
    public function color()
    {
        echo "Set the Heat's Shoes'color as white <br>";
    }
}

/**
 * 游戏球队设置页面,包括了服装设置和球鞋设置
 * Class TeamSetting
 */
abstract class TeamSetting{
    abstract public static function setUniform();
    abstract public static function setShoes();
}

/**
 * 热火队的球队设置页面,有热火球衣和热火球鞋设置
 * Class HeatSetting
 */
class HeatSetting extends TeamSetting
{
    public static function setUniform()
    {
        return new HeatUniform();
    }
    public static function setShoes()
    {
        return new HeatShoes();
    }
}

/**
 * 操作一:进入热火队球衣设置,主场球衣设置为红白,客场球衣设置为红黑
 */
$myControl1 = HeatSetting::setUniform();
$myControl1->home();
$myControl1->away();
/**
 * 操作二:进入热火队球鞋设置,球鞋样式设置为高帮,颜色设置为白色
 */
$myControl2 = HeatSetting::setShoes();
$myControl2->style();
$myControl2->color();