工厂方法模式
面向对象的设计强调“抽象类高于实践”,尽可能的将代码设计的一般化,而非特殊化——也就是降低耦合,提升标准性。于是,前辈们便设计了“特定类处理实例化”的工厂方法。
问题
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
方法了吗?
一旦数据格式增多、功能增加、数据格式间存在业务差异,就会造成一个新问题:大量的判断语句——包括我自己在内的一些人认为,这是代码腐烂的象征。
至少,当重复的代码开始蔓延,我们不应该感到乐观。(尽管下面的处理方法,在一定程度上也重复了这个错误)
面对上述难题,我们有了下列三条要求:
在代码运行时,才能了解我们要生成的对象类型(实时传参,注意那句:
$comms = new CommsManager( CommsManager::MEGA )
);相对轻松的加入新数据格式(Product);
每个产品类型都能轻松加入订制化功能。
既然如此,我们干脆将 数据格式们 从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
功能呢?
答案很明显,我们需要:可以同时处理一组相关实现的架构,解决之道就在下一篇文章:抽象工厂模式。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。