简单工厂模式

基本上每个人手机里都有一款音乐播放器,目前流行的播放器有:QQ音乐、酷狗音乐、搜狗音乐、网易云音乐、天天动听等。下面是一段关于播放音乐的代码:

if ($type == 'QQ') {
    $player = new QQPlayer();
} else if ($type == 'Wy') {
    $player = new WyPlayer();
} else if ($type == 'KG') {
    $player = new KGPlayer();
} else {
    $palyer = null;
}
 
$player->on();  // 打开播放器
$player->choiceMusic('我不配');  // 选择歌曲
$player->play();  // 开始播放

为了时代码的逻辑更加清晰、可读性更好,我们要善于把功能独立的代码块封装成函数。按照这个设计思路,我们可以将其中的条件分支抽离出来,单独放在一个类中的方法中。这个类,我们就可以叫做简单工厂模式。

简单工厂模式的定义:一个类可以根据不同的参数来获取不同的实例,一般这些被创建的实例都具有相同的父类。

静态工厂模式:一般的,我们将简单工厂模式中的用于创建不同实例的方法设置为静态方法,避免创建多个相同实例。

下面我们用简单工厂模式改写上面的代码

class MusicPlayerFactory
{
    public static function create ($type)
    {
        if ($type == 'QQ') {
            $player = new QQPlayer();
        } else if ($type == 'Wy') {
            $player = new WyPlayer();
        } else if ($type == 'KG') {
            $player = new KGPlayer();
        } else {
            $player = null;
        }
        return $player;
    }
}

// 业务代码修改如下
$player = MusicPlayerFactory:create('QQ');
$player->on();  // 打开播放器
$player->choiceMusic('我不配');  // 选择歌曲
$player->play();  // 开始播放

对于上面的简单工厂模式,如果我们需要添加新的音乐播放器,就一定会修改MusicPlayerFactory的create方法,这有点不符合“开闭原则”。对于这种条件分支不是很多,另外类的创建也非常的简单,使用简单工厂模式是完全可以的。如果非要将if分支逻辑去掉,使他符合“开闭原则”,那么就可以使用工厂方法来实现。对于工厂方法,也不是一定比简单工厂模式要好,虽然它的扩展性比较好,但是牺牲了可读性。

工厂方法模式

定义:在工厂方法模式中,工厂父类负责定义创建产品对象的公共接口,而工厂子类则负责生成具体的产品对象,这样做的目的是将产品类的实例化操作延迟到工厂子类中完成,即通过工厂子类来确定究竟应该实例化哪一个具体产品类。

现在我们用“多态”来消除掉上面简单工厂模式的if分支结构。实现的代码如下所示:

interface IMusicPlayerFactory
{
    static function create ();
}

class QQPlayerFactory implements IMusicPlayerFactory
{
    public static function create ()
    {
        return new QQPlayer();
    }
}

class WyPlayerFactory implements IMusicPlayerFactory
{
    public static function create ()
    {
        return new WyPlayer();
    }
}

class KGPlayerFactory implements IMusicPlayerFactory
{
    public static function create ()
    {
        return new KGPlayer();
    }
}

// 业务代码修改如下
if ($type == 'QQ') {
    $player = QQPlayerFactory::create();
} else if ($type == 'Wy') {
    $player = WyPlayerFactory::create();
} else if ($type == 'KG') {
    $player = KGPlayerFactory::create();
} else {
    throw new \Exception('...');
}
$player->on();  // 打开播放器
$player->choiceMusic('我不配');  // 选择歌曲
$player->play();  // 开始播放

可以看到,问题又回到了原点,业务代码里又出现了if条件分支结构。那么怎么去解决该问题呢?

我们可以为工厂类再创建一个简单工厂,用来创建工厂类对象。新的简单工厂代码如下:

class MusicPlayerFactoryMap
{
    const Players = [
        'QQ' => 'QQPlayerFactory',
        'Wy' => 'WyPlayerFactory',
        'KG' => 'KGPlayerFactory'
    ];
    public static function getPlayerFactory (string $type)
    {
        if (empty($type)) {
            return null;
        }
        return (self::Players[$type])::create();
    }
}

// 业务代码修改如下
$palyer = MusicPlayerFactoryMap::getPlayerFactory('QQ')
$player->on();  // 打开播放器
$player->choiceMusic('我不配');  // 选择歌曲
$player->play();  // 开始播放

可以看到,使用了工厂模式,结构变的比之前复杂的多。如果来的创建实例过程复制,我们才会

一文读懂简单工厂、工厂方法、抽象工厂