什么是装饰者设计模式
装饰着模式:简单的一句话理解就是,动态的给一个对象添加一些额外的功能,装饰者模式相对于生成子类更加的灵活
装饰者设计模式UML图
可以通俗理解为
装饰者设计模式的组成
-
Component(抽象构件):它是装饰类和具体构件的公共父类;
-
ConcreteComponent(具体构件):具它是抽象构件对象的子类,用来定义具体的构件对象;
-
Decorator(抽象装饰类):进继承抽象构件,用于给具体的构件添加一些新的职责;
-
ConcreteDecoraror(具体装饰类):实现了抽象装饰类,它负责向构件添加新的职责;
奶茶实例演示
业务场景:现我们现在模拟这样一个场景,我们点了一杯奶茶,然后给奶茶中加了冰块,加了珍珠,最后我们还想再给加点红豆,这里加红豆就使用了装饰者。
Component - 奶茶抽象类
public interface MilkyTea {
public void recipe();
}
public interface MilkyTea {
public void recipe();
}
public class MilkyTeaA implements MilkyTea {
@Override
public void recipe() {
System.out.println("老板来一杯奶茶,加冰块");
}
}
public class MilkyTeaA implements MilkyTea {
@Override
public void recipe() {
System.out.println("老板来一杯奶茶,加冰块");
}
}
public class Decorator implements MilkyTea {
private MilkyTea milkyTea;
public void setMilkyTea(MilkyTea milkyTea) {
this.milkyTea = milkyTea;
}
@Override
public void recipe() {
milkyTea.recipe();
}
}
public class Decorator implements MilkyTea {
private MilkyTea milkyTea;
public void setMilkyTea(MilkyTea milkyTea) {
this.milkyTea = milkyTea;
}
@Override
public void recipe() {
milkyTea.recipe();
}
}
public class MilkyTeaADecorator extends Decorator {
@Override
public void recipe() {
super.recipe();
recipeHD();
}
public void recipeHD() {
System.out.println("老板再给加点珍珠吧");
}
}
public class MilkyTeaADecorator extends Decorator {
@Override
public void recipe() {
super.recipe();
recipeHD();
}
public void recipeHD() {
System.out.println("老板再给加点珍珠吧");
}
}
public class MilkyTeaBDecorator extends Decorator {
@Override
public void recipe() {
super.recipe();
recipeHD();
}
public void recipeHD() {
System.out.println("老板你再给加点红豆吧");
}
}
public class MilkyTeaBDecorator extends Decorator {
@Override
public void recipe() {
super.recipe();
recipeHD();
}
public void recipeHD() {
System.out.println("老板你再给加点红豆吧");
}
}
public class Test {
public static void main(String[] args) {
MilkyTeaA milkyTea = new MilkyTeaA();
MilkyTeaADecorator milkyTeaA = new MilkyTeaADecorator();
MilkyTeaBDecorator milkyTeaB = new MilkyTeaBDecorator();
milkyTeaA.setMilkyTea(milkyTea);
milkyTeaB.setMilkyTea(milkyTeaA);
milkyTeaB.recipe();
}
}
public class Test {
public static void main(String[] args) {
MilkyTeaA milkyTea = new MilkyTeaA();
MilkyTeaADecorator milkyTeaA = new MilkyTeaADecorator();
MilkyTeaBDecorator milkyTeaB = new MilkyTeaBDecorator();
milkyTeaA.setMilkyTea(milkyTea);
milkyTeaB.setMilkyTea(milkyTeaA);
milkyTeaB.recipe();
}
}
public interface Package {
public String title();
public List<String> content();
public Double price();
}
public interface Package {
public String title();
public List<String> content();
public Double price();
}
public class PackageA implements Package {
// 使用单例设计模式
public static class Instance{
public static List<String> content = new ArrayList<>();
public static Double price = 0.0D;
}
@Override
public String title() {
return "套餐A - 典雅黑森林";
}
@Override
public List<String> content() {
Instance.content.add("黑森林门板");
Instance.content.add("黑森林门套");
Instance.content.add("黑森林子门");
Instance.content.add("黑森林门锁");
Instance.content.add("黑森林门窗");
return Instance.content;
}
@Override
public Double price() {
Instance.price += 2506.3;
return Instance.price;
}
}
public class PackageA implements Package {
// 使用单例设计模式
public static class Instance{
public static List<String> content = new ArrayList<>();
public static Double price = 0.0D;
}
@Override
public String title() {
return "套餐A - 典雅黑森林";
}
@Override
public List<String> content() {
Instance.content.add("黑森林门板");
Instance.content.add("黑森林门套");
Instance.content.add("黑森林子门");
Instance.content.add("黑森林门锁");
Instance.content.add("黑森林门窗");
return Instance.content;
}
@Override
public Double price() {
Instance.price += 2506.3;
return Instance.price;
}
}
笃志,励前行。
让我们每天积累一小点知识,争取早日成为技术大牛~
可能这个真实项目小例子不能很好的说明装饰者模式的作用(有一点违反开放-关闭原则),可以看在JDK中的应用就可以得出这个模式的用途以及查看第二节的UML图就可以简单的了解装饰者模式的使用方式了。
本小案例充分使用了之前学习的单例设计模式,建造者设计模式,装饰者设计模式,达到了可以灵活装配套餐中的内容,由于不是实际项目的数据,所以暂时定了三个参数,分别是 套餐名、套餐内容、套餐价格,然后可以很灵活的装配这三个属性的任何内容。但是还是遇到了代码量过多,逻辑比较混乱的弊端,所以欢迎大佬们在下面提出意见~
思考
结果
public class Test {
public static void main(String[] args) {
PackageA packageA = new PackageA();
//删除黑森林门套,增加红珍珠门套
Decorator product = new ProductDecorator().setAddTarget("红珍珠门套").setRemoveTarget("黑森林门套").buildObject();
// 打8折
Decorator price = new PriceDecorator().setPercent(0.8D).buildObject();
// 修改套餐名称
Decorator title = new TitleDecorator().setTargetTitle("套餐A - 红珍珠改款").buildObject();
product.setPackage(packageA);
price.setPackage(product);
title.setPackage(price);
System.out.println("产品名称" + title.title() + "\r\n" + "产品内容" + title.content() + "\r\n" + "产品价格" + title.price());
}
}
测试类
public class TitleDecorator extends Decorator {
private String targetTitle;
public TitleDecorator() {
}
public TitleDecorator(String targetTitle) {
this.targetTitle = targetTitle;
}
@Override
public String title() {
return targetTitle;
}
public TitleDecorator setTargetTitle(String targetTitle) {
this.targetTitle = targetTitle;
return this;
}
// 使用建造者模式
public TitleDecorator buildObject(){
return new TitleDecorator(targetTitle);
}
}
套餐名称装饰子类
public class PriceDecorator extends Decorator {
private Double percent;
public PriceDecorator() {
}
public PriceDecorator(Double percent) {
this.percent = percent;
}
@Override
public Double price() {
return super.price() * percent;
}
public PriceDecorator setPercent(Double percent) {
this.percent = percent;
return this;
}
// 使用建造者模式
public PriceDecorator buildObject(){
return new PriceDecorator(percent);
}
}
价格装饰子类
public class ProductDecorator extends Decorator {
private String removeTarget;
private String addTarget;
public ProductDecorator() {
}
public ProductDecorator(String removeTarget, String addTarget) {
this.removeTarget = removeTarget;
this.addTarget = addTarget;
}
@Override
public List<String> content() {
List<String> content = super.content();
content.remove(removeTarget);
content.add(addTarget);
return content;
}
public ProductDecorator setRemoveTarget(String removeTarget) {
this.removeTarget = removeTarget;
return this;
}
public ProductDecorator setAddTarget(String addTarget) {
this.addTarget = addTarget;
return this;
}
// 使用建造者模式
public ProductDecorator buildObject(){
return new ProductDecorator(removeTarget,addTarget);
}
}
产品装饰子类
public class Decorator implements Package {
private Package p;
public void setPackage(Package p) {
this.p = p;
}
@Override
public String title() {
return p.title();
}
@Override
public List<String> content() {
return p.content();
}
@Override
public Double price() {
return p.price();
}
}
装饰类
套餐A 具体内容
套餐抽象接口
在北京闼闼木门项目这边,本周有一个需求,就是添加一个套餐活动,每个套餐内包含的木门材料都不同,当然价格也不同,并且还要考虑销售范围,每个套餐的销售范围不同,每个产品的销售范围不同,要同时符合才能进行套餐打包,并且套餐内的产品还有可能降价,这边开会的讨论结果争吵的不可开交,有人提议直接使用Json数据写死,价格也写死,我虽然不负责块项目,但是我感觉这样写比较Low,其实这个需求使用装饰者编码模式可以很好解决,下面我来演示一下如何使用装饰者模式来解决这个需求(非业务代码,只是想法的实现)
真实项目的思考
说起装饰者模式在JDK中的应用,我们在学习Java SE阶段的IO流时就遇到过,不知道大家还记得InputStream不?当我们有特殊需要,我们还可以使用
BufferedInputStream
完成了缓存功能,使读取文件速度大大提升
DataInputStream
把byte转换成Java基本数据类型
FileInputStream
读取文件
等等等(详情见下图),以下这几个类就是对InputStream类的装饰,达到了不同用途的作用。
装饰者模式在JDK中的应用
结果:
测试