装饰模式 -- 设计模式系列文章(三)
- 概述
在日常开发工作中,适当的使用一些设计模式,可以让代码扩展性更强,能更好地拥抱变化,让代码更加优雅。本文主要介绍设计模式中的装饰模式,并附上测试示例 Demo 供大家参考。
- 定义
装饰模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。
- 个人理解
装饰模式可以在运行时,在不修改原有代码的前提下,动态地为对象添加或移除指定的业务逻辑,比起继承,采用装饰模式让代码更据扩展性,更为灵活,避免了继承导致的代码耦合。
装饰模式的设计理念是:对修改关闭,对扩展开放;面对接口编程。
- 示例介绍
个人有时间会自己煮汤(这不是重点哈,只是刚好有这么一个场景适合采用装饰模式,因此也就拿来做例子了),在煮汤的过程中,我们需要添加不同的食材,就拿凉瓜排骨汤来说吧,需要的食材有排骨、凉瓜、大豆、食用盐、水等,当然根据不同人的不同喜好,可以添加其他的食材。以上说到的这些食材都是制作“凉瓜排骨汤”所必须的,没有它们的点缀,汤喝起来也就不是那个味道了,这里的点缀我们可以理解为装饰,是食材装饰了“凉瓜排骨汤”,也就是说食材是装饰者,而“凉瓜排骨汤”是被装饰的对象。UML 图如下:
从上面的 UML 图可以看出,菜式类 Dish 和 食材抽象类 AbSeasoning 都需要实现食材接口 IMaterial 。菜式类 Dish 定义了一个属性 name ,用于存储菜式名称,食材抽象类 AbSeasoning定义了一个食材引用 material 、食材名称 name 和食材分量 weight ,分别用于存储食材引用、食材名称和食材分量。食材类 Seasoning 则需要继承食材抽象类 AbSeasoning ,菜式类 Dish 和食材类 Seasoning 都需要实现各自获取食材的方法 getStuff。在运行的过程中,只需要把菜式赋给食材的食材引用即可,如果后续还需要添加其他的食材,可以把最后一次添加的食材赋给新食材的食材引用即可达到动态添加的效果。
- 示例代码
IMaterial 食材接口类(装饰者接口)
package decorator; public interface IMaterial { public String getStuff(); }
Dish 菜式类(被装饰对象)
package decorator; public class Dish implements IMaterial { private String name; @Override public String getStuff() { return this.getName(); } public String getName() { return name; } public void setName(String name) { this.name = name; } }
AbSeasoning 食材抽象类(装饰者抽象类)
package decorator; public abstract class AbSeasoning implements IMaterial { private IMaterial material; private String name; private String weight; public IMaterial getMaterial() { return material; } public void setMaterial(IMaterial material) { this.material = material; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getWeight() { return weight; } public void setWeight(String weight) { this.weight = weight; } }
Seasoning 食材类(装饰者)
package decorator; public class Seasoning extends AbSeasoning { @Override public String getStuff() { return this.getMaterial().getStuff() + "\n" + this.getName() + ":" + this.getWeight(); } }
TestMain 测试类
package test; import decorator.AbSeasoning; import decorator.Dish; import decorator.Seasoning; public class TestMain { public static void main(String[] args) { Dish dish = new Dish(); dish.setName("凉瓜排骨汤"); AbSeasoning spareribs = new Seasoning(); spareribs.setName("排骨"); spareribs.setWeight("500g"); spareribs.setMaterial(dish); AbSeasoning bitterGourd = new Seasoning(); bitterGourd.setName("凉瓜"); bitterGourd.setWeight("200g"); bitterGourd.setMaterial(spareribs); AbSeasoning soybean = new Seasoning(); soybean.setName("大豆"); soybean.setWeight("50g"); soybean.setMaterial(bitterGourd); AbSeasoning salt = new Seasoning(); salt.setName("食用盐"); salt.setWeight("5g"); salt.setMaterial(soybean); AbSeasoning water = new Seasoning(); water.setName("水"); water.setWeight("500ml"); water.setMaterial(salt); System.out.println(water.getStuff()); } }
测试结果