然而在实际应用中,很可能产品是一个多层次的树状结构,这时候由于简单工厂模式中只有一个工厂类来对应这些产品,所以这可能会把我们的上帝累坏了,因此简单工厂模式只适用于业务简单的情况下或者具体产品很少增加的情况,而对于复杂的业务环境可能不太适应了,这个时候就应该由工厂方法模式来出场了。
又来新需求了,那就是每种格式的预约数据中,需要提供页眉和页脚来描述每次预约。
咱们先来用简单工厂模式来实现上述功能,如下:
// 简单工厂模式 class CommsManager { const BLOGGS = 1; const MEGA = 2; private = $mode; public function __construct( $mode ) { $this->mode = $mode; } // 生成解码器对应的页眉 public function getHeaderText() { switch( $this->mode ) { case ( self::MEGA ): return "MegaCal header\n"; default: return "BloggsCal header\n"; } } // 生成解码器 public function getApptEncoder() { switch( $this->mode ) { case ( self::MEGA ): return new MegaApptEncoder(); default: return new BloggsApptEncoder();; } } }
从上述代码中,我们可以看到,相同的条件语句switch在不同的方法中出现了重复,而且如果添加新的数据格式,那么需要改动的类过多。所以需要对我们的结构进行修改,以求更容易扩展和维护。
我们可以使用创造者子类分别生成对应的产品,这样添加新的数据格式时,只需要添加一个创造者子类即可,方便扩展和维护,这也就是比简单工厂模式更复杂一点的工厂模式,如下:
// 工厂模式 abstract class CommsManager { abstract function getHeaderText(); abstract function getApptEncoder(); abstract function getFooterText(); } class BloggsCommsManager extends CommsManager { function getHeaderText() { return "BloggsCal Header\n"; } function getApptEncoder() { return new BloggsApptEncoder(); } function getFooterText() { return "BloggsCal Footer\n"; } } class MegaCommsManager extends CommsManager { function getHeaderText() { return "MegaCal Header\n"; } function getApptEncoder() { return new MegaApptEncoder(); } function getFooterText() { return "MegaCal Footer\n"; } }
在这个时候,如果有新的数据格式,只需要添加一个创造类的子类即可,例如此时想获取MegaCal对应的解码器直接通过MegaCommsManager::getApptEncoder()
获取即可。
完了么?我只能说,这个实例还没有完事。
新需求又来了,那就是不仅需要和A公司交流预约数据(Appointment),还需要交流待办事宜(Ttd)、联系人(Contact)等数据,同样的这些数据交流的格式也是BloggsCal和MegaCal,这个时候,我们可以直接在对应解码器的子类中添加处理事宜(TtD)和联系人(Contact)的方法,如下: