装饰模式

2023-07-16

之前写Python的时候,用到了装饰器,然后不由自主想到了设计模式中的装饰模式,正好最近一直在看设计模式,就记录下自己的一些理解。如有错误,欢迎指正。

装饰模式

比较官方的定义是这样的

装饰模式是在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象

装饰,装饰嘛,很显然你只想让别人看见最终装饰的东西,并不像让别人看见你的装饰过程。打个比方说,你去餐馆吃饭,很明显主要是吃饭,当然光吃米饭,肯定难以下咽了,你想了想准备吃青椒肉丝盖浇饭,然后餐馆厨房开始做了,这个时候你想去厨房去瞧一瞧,你发现厨房门上贴着“非工作人员入内”,意思很明显嘛,餐馆并不希望我去参观他做盖浇饭的过程,谁知道中间有什么猫腻,最后呈现在我面前的就是我自己点的那份青椒肉丝盖浇饭,当然盖浇饭不止这一种还有番茄鸡蛋盖浇饭啊,鱼香肉丝盖浇饭啊。。。不过厨房加工的地方不让看。

类图是这样的:

可以把Component看作一个食物,ConcreteComponent看作米饭,Decorator则可以看作加工过程,ConcreteDecoratorA(B)就是各种食物素材啦。
对于Componet是无需知道Decorator存在的。
如果只有一个ConcreteComponent类而没有抽象的Component类,那么Decorator类可以是ConcreteComponent的一个子类。同样如果只有一个ConcreteDecorator
,那么也没有必要建立一个单独的Decorator,而可以把Decorator和ConcreteDecorator的责任合并成一个类。

这个时候的类图,如下:

和这样

顺便说下starUML类图中部分符号的含义:

  1. 类 ( Class ) :三层矩形框表示,第一层类名,如果是抽象类,是用斜体表示的;第二层是字段和属性;第三层则是类方法,其中"+"表示该方法是public的,“-”表示private,“#”表示protected
  2. 继承类():用空心三角形+实现来表示

Talk is cheap,show me the code:

<?php
abstract class food
{
	public $_name;
	abstract public function cook();
}

//被装饰的类
class rice extends food
{
	public function __construct()
	{
		$this->_name = 'rice';
	}

	public function cook()
	{
		return "cooked_rice";
	}
}

//盖浇饭decorator
class fooddecorator extends food
{
	public function __construct()
	{
		$this->_name = 'fooddecorator';
	}

	public function cook(){
		return "cooking";
	}
} 

//青椒
class  capsicum extends fooddecorator
{
	public $_food;
	public function __construct($food)
	{
		$this->_name = 'capsicum';
		if($food instanceof food)
		{
			$this->_food = $food;
		}else{
			return false;
		}
	}

	public function cook()
	{
		return $this->_food->cook()." cooked_capsicum";
	}
}

//肉丝
class meat extends fooddecorator
{
	public $_food;
	public function __construct($food)
	{
		$this->_name = 'meat';
		if($food instanceof food)
		{
			$this->_food = $food;
		}else{
			return false;
		}
	}

	public function cook()
	{
		return $this->_food->cook()." cooked_meat";
	}
}

//qr 青椒加饭 qjrs means 青椒肉丝
$rice = new rice();
//加青椒
$qr = new capsicum($rice);
//加肉
$qjrs = new meat($qr);

echo "fried green peppers:".$qjrs->cook();
?>

结果:

好像忘记加盐了0.0
装饰模式是围绕一个核心,然后动态装饰这个核心,很明显盖浇饭的核心是饭,在饭上加不同的菜,产生了不同的盖浇饭
这个时候估计有人说了,我不用装饰模式也可以实现着个过程啊

然后写了这样的代码:

<?php
abstract class food
{
	public $_name;
	abstract public function cook();
}

class rice extends food
{
	public function __construct()
	{
		$this->_name = 'rice';
	}

	public function cook()
	{
		return " cooked_rice";
	}
}

class capsicum extends food
{
	public function __construct()
	{
		$this->_name = 'capsicum';
	}

	public function cook()
	{
		return " cooked_capsicum";
	}
}

class meat extends food
{
	public function __construct()
	{
		$this->_name = 'meat';
	}

	public function cook()
	{
		return " cooked_meat";
	}
}

$rice = new rice();
$capsicum = new capsicum();
$meat = new meat();
echo "fried green peppers:".$rice->cook().$capsicum->cook().$meat->cook();
?>

结果很明显跟上面一样但是没有区别吗很明显第二种方法,第二种方法暴露了你做盖浇饭的顺序,就相当于顾客悄悄溜进厨房,看着厨师把盖浇饭做好了,见证了这一从无到有的过程,因此我们需要用装饰模式,将这加工处理的工程隐藏起来,只有这一个区别吗?很明显并不是,第二种方法三个子类处于同级,不分主次,但是不要忘了,顾客是去吃米饭的,之所以要了盖浇饭是为了让米饭更好吃,如果真的没有菜,做不成盖浇饭,只吃米顾客还是可以接受的,但是要是没有米,光有菜可不行,米饭是前提条件。要是觉得难以接受这个设定,就把盖浇饭换成各种类型的粥吧,绿豆粥,红薯粥。。。,要是少了米,还能叫粥吗。

最后做下简单的总结

装饰模式总结

1.装饰模式是向原有的类,动态的添加新的功能。
2.把类中的装饰功能从类中移去,是简化了原有类。
3.跟继承的区别,装饰模式是围绕着一个核心,在该核心存在的情况下去进行的,存在主次的关系,而继承所有子类处于同级
4.装饰模式的装饰顺序也是很重要,继承则不存在这种问题。比如说,穿衣服,你应该先穿内衣,再穿外套,而不是任意顺序。
5.多用组合,少用继承