Decorator Pattern装饰模式
装饰模式:动态的给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更为灵活。
装饰,让我们想到了房子的装修、人的打扮、手机装饰…等等,但是,不管怎么装修,房子还是那个房子、墙还是那堵墙,人还是那个人,脸还是那张脸;手机还是那部手机,屏幕还是那个屏幕,只是给人的感觉变了而已。
如果拿手机来说,我们买了手机后,都会给手机贴膜、安个吊坠、装个边框什么的,下面我们看看我们最常用的手机是如何贴膜的:
public class Phone {
//构造方法
public Phone(String name) {
this.name = name;
}
//名称
private String name;
//贴膜
public void film(){
System.out.print(" 贴膜 ");
}
//最后的外观
public void exterior(){
System.out.println(" 手机:"+ name + "的样子!");
}
//getter & setter
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class PhoneTest {
public static void main(String[] args) {
Phone p = new Phone("Iphone5");//一台Iphone5
p.film(); //贴膜
p.exterior(); //我们看到的样子
}
}
这样设计是满足了要求,可是大家都知道,手机装饰有很多种,除了贴膜,还有吊坠、边框、后盖、贴纸….等等,难道新需求来的时候,去修改Phone这个类吗?
根据我们的设计原则:“只对扩展开放,对修改关闭”。就是根据变化去设计一个程序,以后有变化的时候,之扩展我们的程序就行了,不需要修改原来的程序。这就是设计模式的精髓所在。
根据以上的例子,我们要考虑,手机装饰随时可能变化,当你逛街时、浏览某个网站时,看到一款比较新奇的手机配件,也许你会更换装饰了!
我们可能想到了接口:把手机的装饰用一个接口来表示:让所有手机配件来继承这个接口:
//手机
public class Phone {
//构造方法
public Phone(String name) {
this.name = name;
}
//名称
private String name;
//外观
private List<Accessories> accessories = new ArrayList<Accessories>();
// 外观展示
public void exterior(){
for(Accessories accessorie : accessories){
accessorie.exterior();
}
System.out.println(" 手机:"+ name + "的样子!");
}
//getter & setter
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<Accessories> getAccessories() {
return accessories;
}
public void setAccessories(List<Accessories> accessories) {
this.accessories = accessories;
}
}
//配件接口
public interface Accessories {
void exterior();
}
//贴膜
public class Film implements Accessories {
public void exterior() {
System.out.print(" 贴膜 ");
}
}
//金属边框
public class MetalFrame implements Accessories {
public void exterior() {
System.out.print(" 金属边框 ");
}
}
//手机测试
public class PhoneTest {
public static void main(String[] args) {
Phone p = new Phone("Iphone5");//一台Iphone5
//配件
Accessories a = new Film();
Accessories b = new MetalFrame();
List<Accessories> l = new ArrayList<Accessories>();
l.add(a);
l.add(b);
p.setAccessories(l);
// 外观展示
p.exterior();
}
}
这样似乎解决了问题,可是代码看起来非常复杂,特别是配件在装饰的时候,都要装到配件列表中。有没有更简洁的方式呢?
好吧,还是让我们用装饰模式优雅的解决这个问题吧:
先看类图:
代码如下:
//手机接口
public interface Phone {
public void exterior();
}
//具体的对象 --实现了手机接口
public class Iphone5 implements Phone {
public void exterior() {
System.out.print("Iphone5外观展示!");
}
}
//手机配件抽象类 ---同样实现手机接口
public abstract class Accessories implements Phone {
//要装饰的对象 -- 手机
private Phone phone;
public void setPhone(Phone phone) {
this.phone = phone;
}
//调用传入的Phone的exterior方法
public void exterior() {
if(phone != null){
phone.exterior();
}
}
}
//具体装饰类:贴膜 -- 继承自配件类
public class Film extends Accessories {
// 重写父类中的外观展示方法,
// 在父类方法调用前加入自己的处理逻辑
public void exterior() {
System.out.print(" 贴膜 ");
super.exterior();
}
}
//具体装饰:金属边框 – 继承自配件类
public class MetalFrame extends Accessories {
// 重写父类中的外观展示方法
// 在父类方法调用前加入自己的处理逻辑
public void exterior() {
System.out.print(" 金属边框 ");
super.exterior();
}
}
//代码测试
public class PhoneTest {
public static void main(String[] args) {
// 一台Iphone5
Iphone5 iphone5 = new Iphone5();
//配件
Film film = new Film();
MetalFrame metalFrame = new MetalFrame();
// 外观展示
film.setPhone(iphone5);
metalFrame.setPhone(film);
metalFrame.exterior();
}
}
是不是感觉代码更加简洁了,而且也更加灵活了。这是装饰模式在程序设计中发挥的重要作用。
如果还不明白,我们可以举一个非常简单的例子,我们都吃过火锅,比如呷哺火锅,火锅点餐系统就是你点一份锅底,然后选择几份配菜,配菜可以随便点、而且可以点多份,锅底就一个,到结账的时候要知道点了那些东西,然后得花多少钱。根据这个需求,我们设计的时候用装饰模式来解决:
建立一个食品接口,有一个获得食品名称的方法,一个计算价格的方法和另外一个通过传入其它食品计算价格的方法,然后具体对象是锅底,因为菜都是在锅底里面涮的,没有锅底肯定不行,而配菜是跟随锅底随意搭配的,配菜可以多点,而且可以点多份。建一个锅底类,给出具体名称和具体价格,和一个配菜抽象类,让锅底类和配菜都继承这个食品接口,然后让具体的配菜都继承配菜类,在配菜里给出配菜具体名称和具体价格。类图如下:
这样设计的好处是锅底的配菜能随意搭配,可多选可复选,而且以后如果增加另外一种锅底的时候,只需要去继承这个食品接口就可以了,配菜在扩展的时候只需要把新上市的蔬菜去继承配菜就行了,并不需要去更改以前写好的代码,这就遵循了设计原则的:“针对接口编程”和“对扩展开放,对修改关闭”的原则了。看看下面的代码,让我们开始点餐吧:
//食品接口
public interface Food {
String getName();
int price();
int price(Food food);
}
//配菜抽象类
public abstract class Materials implements Food {
public abstract String getName();
}
//锅底类—具体对象
public class Spicy implements Food {
private String name;
//构造方法
public Spicy(String name){
this.name = name;
}
//食品名称
public String getName(){
return name;
}
//价格
public int price() {
return 15;
}
public int price(Food food) {
return food.price() + 15;
}
}
//粉丝 --继承自配菜
public class Fans extends Materials {
private Food food;
//构造方法
public Fans(Food food) {
this.food = food;
}
public String getName() {
return food.getName() + ",粉丝";
}
public int price() {
return food.price() + 3;
}
public int price(Food food) {
return food.price() + 3;
}
}
//白菜类 – 继承自配菜
public class Cabbage extends Materials {
private Food food;
//构造方法
public Cabbage(Food food) {
this.food = food;
}
public String getName() {
return food.getName() + ",白菜";
}
//白菜价格
public int price() {
return food.price() + 2;
}
//
public int price(Food food) {
return food.price() + 2;
}
}
//牛肉类 – 继承自配菜
public class Beef extends Materials {
private Food food;
//构造方法
public Beef(Food food) {
this.food = food;
}
public String getName() {
return food.getName() + ",牛肉";
}
public int price() {
return food.price() + 10;
}
public int price(Food foods) {
return foods.price() + 10;
}
}
//五花肉类 – 继承自配菜
public class Pork extends Materials {
private Food food;
//构造方法
public Pork(Food food) {
this.food = food;
}
public String getName() {
return food.getName() + ",五花肉";
}
public int price() {
return food.price() + 8;
}
public int price(Food food) {
return food.price() + 8;
}
}
//豆皮类 – 继承自配菜类
public class Yuba extends Materials {
private Food food;
//构造方法
public Yuba(Food food) {
this.food = food;
}
public String getName() {
return food.getName() + ",豆皮";
}
public int price() {
return food.price() + 5;
}
public int price(Food foods) {
return foods.price() + 5;
}
}
//测试类
public class FoodTest {
public static void main(String[] args) {
// 香辣锅底的点餐
Spicy spicy = new Spicy("香辣锅底");
Fans fans = new Fans(spicy);
Yuba yuba = new Yuba(fans);
Beef beef = new Beef(yuba);
Cabbage cabbage = new Cabbage(beef);
System.out.println( "您要点的菜是:" + cabbage.getName()
+ ",合计:" + cabbage.price()+ "元。");
// 清汤锅底的点餐
Spicy spicy2 = new Spicy("清汤锅底");
Pork pork = new Pork(spicy2);
Fans fans2 = new Fans(pork);
Fans fans3 = new Fans(fans2);
Cabbage cabbage2 = new Cabbage(fans3);
System.out.println( "您要点的菜是:" + cabbage2.getName()
+ ",合计:" + cabbage2.price()+ "元。");
}
}
看看装饰模式的定义:动态的给一共对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更为灵活。
在看看我们的程序,锅底对象可以动态的添加配菜,并且能动态的计算出当前点餐的价格。比起生成一个单独配菜的子类然后计算价格更加灵活。