装饰模式
组合模式帮助我们聚合组件,而装饰模式则使用类似结构来帮助我们改变具体组件的功能。
装饰模式使用组合和委托而不是只使用继承来解决功能变化的问题。实际上,Decorator类会持有另外一个类的实例。Decorator对象会实现与被调用对象的方法相对应的类方法。用这种办法可以在运行时创建一系列的Decorator对象。
abstract class Tile
{
abstract function getWealthFactor();
}
class Plains extends Tile
{
private $wealthfactor = 2;
function getWealthFactor()
{
return $this->wealthfactor;
}
}
abstract class TileDecorator extends Tile
{
protected $tile;
function __construct(Tile $tile)
{
$this->tile = $this;
}
}
class DiamondDecorator extends TileDecorator
{
function getWealthFactor()
{
return $this->tile->getWealthFactor() + 2;
}
}
class PollutionDecorator extends TileDecorator
{
function getWealthFactor()
{
return $this->tile->getWealthFactor() - 4;
}
}
$tile = new Plains();
print $tile->getWealthFactor();//2
$tile = new DiamondDecorator(new Plains());
print $tile->getWealthFactor();//4
$tile = new PollutionDecorator(new DiamondDecorator(new Plains()));
print $tile->getWealthFactor();//0
通过像这样使用组合和委托,可以在运行时轻松地合并对象。因为模式中所有对象都扩展自Tile,所以客户端代码并不需要知道内部是如何合并的。
本例的类图如下:
这样的模型极具扩展性。我们可以非常轻松地添加新的装饰器或新的组件。通过使用大量装饰器,我们可以在运行时创建极为灵活的结构。
装饰模式建立的管道对于创建过滤器非常有用。客户端程序员可将核心组件与装饰对象合并,从而对核心方法进行过滤、缓冲、压缩等操作。下面是一个使用装饰模式的web请求实例:
class RequestHelper
{
}
abstract class ProcessRequest
{
abstract function process(RequestHelper $req);
}
class MainProcess extends ProcessRequest
{
function process(RequestHelper $req)
{
print __CLASS__ . ":doing something useful with request\n";
}
}
abstract class DecorateProcess extends ProcessRequest
{
protected $processrequest;
function __construct(ProcessRequest $pr)
{
$this->processrequest = $pr;
}
}
class LogRequest extends DecorateProcess
{
function process(RequestHelper $req)
{
print __CLASS__ . ":logging request\n";
$this->processrequest->process($req);
}
}
class AuthenticateRequest extends DecorateProcess
{
function process(RequestHelper $req)
{
print __CLASS__ . ":authenticating request\n";
$this->processrequest->process($req);
}
}
class StructureRequest extends DecorateProcess
{
function process(RequestHelper $req)
{
print __CLASS__ . ":structuring request\n";
$this->processrequest->process($req);
}
}
$process = new AuthenticateRequest(new StructureRequest(new LogRequest(new MainProcess())));
$process->process(new RequestHelper());
一定要记住:组合和继承通常都是同时使用的。因为装饰对象作为子对象的包装,所以保持基类中的方法尽可能少是很重要的。如果一个基类具有大量的特性,那么装饰对象不得不为它们包装的对象的所有public方法加上委托。你可以用一个抽象的装饰类来实现,不过这仍旧会带来耦合,并可能导致bug的出现。