数字营销 · Web开发 · 基础设施

PHP, Python和Javascript的装饰模式

通过PHP、Python和Javascript的实例,简单介绍装饰模式的基本原理,以及如何通过对象包装和功能扩展实现更加灵活的程序设计。

修饰模式(Decorator Pattern),又叫装饰者模式,是面向对象编程领域中一种动态地为对象添加新行为的设计模式。就功能而言,修饰模式相比生成子类更加灵活,它可以给某个对象,而不是整个类添加一些功能。装饰模式非常适用于灵活扩展对象的功能,下面是装饰模式的UML图:

装饰者模式

例如,有一个技术论坛,用户通过留言进行沟通。由于刚开始论坛里都是熟人,几乎不需要对留言内容进行审核,接收留言的页面可以是这样:

class SaveMsg {
private $msg;

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

public function __store(){
//存入数据库
}
}

后来,随着论坛逐渐出名,就有一些人在上面发链接,需要对含有链接的消息进行过滤。论坛进一步发展,发现除了发垃圾链接的外,还有很多无用的灌水,到后来可能还会出现攻击等各种不正常的帖子。

所以对论坛帖子的管理,可以单独抽象出一个类进行管理。当需要扩充过滤规则时,可以进行动态扩充。

//基类
abstract class Filter{
abstract public function isForbid();
}

//基础过滤类
class MsgFilter extends Filter{
public $content;

public function __construct($msg){
$this->content = $msg;
}

public function isForbid(){
if(preg_match("/https?/i", $this->content)){
return [true, "Not Allowed Urls"];
}else{
return [false];
}
}
}

//装饰器,用来扩充功能
abstract class FilterDecorator extends Filter{
protected $obj;

public function __construct(Filter $obj){
$this->obj = $obj;
}
}

//新过滤器,判断是否重复发帖
class Repeat extends FilterDecorator{
public function isForbid(){
if($this->obj->isForbid()[0] === true){
//判定是否包含URL
return $this->obj->isForbid();
}else if($this->obj->content == "this is a test"){
//判定是否重复发帖
return [true, "Repeat Posts"];
}else{
return [false];
}
}
}

$test = new MsgFilter("httpsfdjoafdsajof");
print_r($test->isForbid());//被禁止

$test2 = new Repeat(new MsgFilter("this is a test"));
print_r($test2->isForbid());//被禁止

在Python中,不需要像PHP一样定义抽象类和抽象方法,实现起来更加简单:

#!/usr/bin/env python

class Filter():
pass

class MsgFilter(Filter):
def __init__(self, msg):
self.content = msg

def isForbid(self):
if 'http' in self.content:
return [True, "Not Allowed Urls"]
else:
return [False]

class FilterDecorator(Filter):
def __init__(self, obj):
self._obj = obj

class Repeat(FilterDecorator):
def isForbid(self):
if self._obj.isForbid()[0]:
return self._obj.isForbid()
elif self._obj.content == 'this is a test':
return [True, "Repeat Posts"]
else:
return [False]

test = MsgFilter("this is a content have http urls")
print test.isForbid()

test2 = Repeat(MsgFilter('this is a test'))
print test2.isForbid()

Javascript中没有传统意义上的类,所有继承都基于原型,理解起来会稍费一些功夫:

function MsgFilter(msg){
this.content = msg;

this.isForbid = function(){
if(this.content.match(/http/g)){
return [true, "Not Allowed Urls"];
}else{
return [false];
}
}
}

function Repeat(obj){
var _obj = obj;

this.isForbid = function(){
if(_obj.isForbid()[0] === true){
return _obj.isForbid();
}else if(_obj.content == 'this is a test'){
return [true, "Repeat Posts"];
}else{
return [false];
}
}
}

var test = new MsgFilter("this is a content have http urls");
console.log(test.isForbid());

var test2 = new Repeat(new MsgFilter("this is a test"));
console.log(test2.isForbid());

由于Javascript缺少传统类的特性,继承对它来说就显得没有那么直接,上面的代码看起来更像是对两个函数的包装和处理。

在Python中,还有一种更加简单的方式为函数添加装饰器,可以直接通过“@”语法对函数进行包装,从而达到扩展功能的目的,例如:

def Decorator(F):
def newF(age):
print "You Are Calling", F.__name__
F(age)
return newF

@Decorator
#通过@给函数showAge添加装饰器Decorator
def showAge(age):
print "hello, i am %d years old" % age

showAge(10)

装饰模式的目的是解决动态扩展功能的问题,它的本质是对对象进行灵活处理。理解装饰模式,不仅有利于深入了解面向对象程序设计,也能提高编程中的抽象和扩展能力。

评论0

欢迎分享你的看法,也欢迎补充不同的实践经验。