工厂方法模式

面向对象的设计强调“抽象类高于实践”,尽可能的将代码设计的一般化,而非特殊化——也就是降低耦合,提升标准性。于是,前辈们便设计了“特定类处理实例化”的工厂方法。

问题

Appointment类需要解析BloggsCal数据格式,在类内部,我们将数据格式写死为BloggsCal,这将会造成耦合,而且实际上,未来业务需求要面临更多的数据格式。

实现

这个时候我们引入工厂方法模式,设置CommsManager类(创造者,creater)、ApptEncoder类(产品,Product)。

接下来,用户只需要调用CommsManager对象的getApptEncoder方法,就可以得到他们需要的对象,而无需关注这个对象到底是哪种数据格式。

abstract class ApptEncoder {
    abstract function encode();
}

class BloggsApptEncoder extends ApptEncoder {
    function encode()
    {
        return "数据格式为BloggsCal。";
    }
}

class MegaApptEncoder extends ApptEncoder {
    function encode()
    {
        return "数据格式为MegaCal。";
    }
}

class CommsManager {
    const BLOGGS = 1;
    const MEGA = 2;
    private $mode = 1;

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

    function getApptEncoder() {
        switch ( $this->mode ) {
            case ( self::MEGA ) :
                return new MegaApptEncoder();
            default:
                return new BloggsApptEncoder();
        }
    }

    function getHeaderText() {
        switch ( $this->mode ) {
            case ( self::MEGA ):
                return "Mega格式的表头";
            default:
                return "BloggsCal格式的表头";
        }
    }

    function getFootText() {
        switch ( $this->mode ) {
            case ( self::MEGA ):
                return "Mega格式的脚页";
            default:
                return "BloggsCal格式的脚页";
        }
    }
}

$comms = new CommsManager( CommsManager::MEGA );
$apptEncoder = $comms->getApptEncoder();
echo $apptEncoder->encode();

注意到CommsManager类的三个getXXX方法了吗?

一旦数据格式增多、功能增加、数据格式间存在业务差异,就会造成一个新问题:大量的判断语句——包括我自己在内的一些人认为,这是代码腐烂的象征。

至少,当重复的代码开始蔓延,我们不应该感到乐观。(尽管下面的处理方法,在一定程度上也重复了这个错误

面对上述难题,我们有了下列三条要求:

  1. 在代码运行时,才能了解我们要生成的对象类型(实时传参,注意那句:$comms = new CommsManager( CommsManager::MEGA ));

  2. 相对轻松的加入新数据格式(Product);

  3. 每个产品类型都能轻松加入订制化功能。

既然如此,我们干脆将 数据格式们 从CommsManager中剥离出来,形成单独的子类。

// 产品抽象类、子类的代码,同上。

abstract class CommsManager {
    abstract function getApptEncoder();
    abstract function getHeaderText();
    abstract function getFooterText();
}

class BloggsCommsManager extends CommsManager {
    function getApptEncoder() {
        return new BloggsApptEncoder();
    }

    function getHeaderText() {
        return "BloggsCal格式的表头";
    }

    function getFooterText() {
        return "BloggsCal格式的脚页";
    }
}

class MegaCommsManager extends CommsManager {
    function getApptEncoder() {
        return new MegaApptEncoder();
    }

    function getHeaderText() {
        return "MegaCal格式的表头";
    }

    function getFooterText() {
        return "MegaCal格式的脚页";
    }
}

$comms = new BloggsCommsManager();
$apptEncoder = $comms->getApptEncoder();
echo $apptEncoder->encode();

结果

这样满足了上述的三个要求,可我想,聪明的你已经注意到了:一定程度上,创建者/产品们 产生了代码重复。而且,问题被转移到了工厂方法中,我们目前只是处理了Appointment功能,如果我们加入TodoList功能呢?

答案很明显,我们需要:可以同时处理一组相关实现的架构,解决之道就在下一篇文章:抽象工厂模式。

面向对象设计模式 - 目录


UioSun
603 声望33 粉丝

use google find the world. "该用户太懒", dead was yesterday.