Java设计模式:装饰者模式(Decorator Pattern)

本文介绍装饰者模式在咖啡店场景的应用,通过实例演示如何利用该模式动态增加对象功能,提高软件设计灵活性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

装饰者模式,涉及的重要设计原则:类应该对扩展开放,对修改关闭

装饰者模式定义:

装饰者模式动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案。

UML类图:


装饰者模式事例:

咖啡店

咖啡种类:

1)深焙咖啡(DarkRoast)

2)家庭混合咖啡(HouseBlend)

3)浓咖啡(Espresso)

4)低咖啡因咖啡(Decaf)

5)其它咖啡。。。

咖啡调料:

1)摩卡(Mocha)

2)牛奶(Milk)

3)豆浆(Soy)

4)奶泡(Whip)

5)其它调料。。。

点单:

要一份加了摩卡和奶泡的深焙咖啡...等等

UML类图


代码实现:

Beverage.java

package com.jing.decorator;

/**
 * 所有饮料的抽象超类
 * @author LiuJing
 *
 */
public abstract class Beverage {

	/**
	 * 饮料的名字
	 */
	String description = "Unknow Beverage";
	
	/*
	 * 获取饮料的名字
	 */
	public String getDescription(){
		return description;
	}
	
	
	/***
	 * 获取饮料的价格
	 * @return 返回计算后的总价
	 */
	public abstract double cost();
	
}

DarkRoast.java

package com.jing.decorator;

import java.text.DecimalFormat;

/***
 * 深烤咖啡
 * @author LiuJing
 *
 */
public class DarkRoast extends Beverage {

	/***
	 * 构造时确定名字
	 */
	public DarkRoast(){
		description = "DarkRoast";
	}
	
	/***
	 * 价格
	 */
	public double cost() {
		double cost = 10.0;
		
		// 用于保存2位小数
		DecimalFormat df = new DecimalFormat("#.00");
		return Double.valueOf(df.format(cost));
	}
}

Decaf.java

package com.jing.decorator;

import java.text.DecimalFormat;

/***
 * 低咖啡因咖啡
 * @author LiuJing
 *
 */
public class Decaf extends Beverage {

	/***
	 * 构造时确定名字
	 */
	public Decaf() {
		description = "Decaf";
	}
	
	/***
	 * 价格
	 */
	public double cost() {
		double cost = 11.0;

		DecimalFormat df = new DecimalFormat("#.00");
		return Double.valueOf(df.format(cost));
	}
}

Espresso.java

package com.jing.decorator;

import java.text.DecimalFormat;

/***
 * 浓咖啡
 * @author LiuJing
 *
 */
public class Espresso extends Beverage {

	/***
	 *  构造时确定其名字
	 */
	public Espresso(){
		description = "Espresso";
	}
	
	/***
	 * 方法返回 该饮料的价格
	 */
	public double cost() {
		double cost = 12.0;
		
		DecimalFormat df = new DecimalFormat("#.00");
		return Double.valueOf(df.format(cost));
	}
}

HouseBlend.java

package com.jing.decorator;

import java.text.DecimalFormat;

/***
 * 家庭混合咖啡
 * @author LiuJing
 *
 */
public class HouseBlend extends Beverage {

	/***
	 * 构造时确定其名字
	 */
	public HouseBlend(){
		description = "HouseBlend";
	}
	
	/***
	 * 返回该咖啡的价格
	 */
	public double cost() {
		double cost = 13.0;
		
		DecimalFormat df = new DecimalFormat("#.00");
		return Double.valueOf(df.format(cost));
	}

}
CondimentDecorator.java

package com.jing.decorator;

/***
 * 调料装饰者抽象超类,继承至 Beverage,便于类型的统一,能彼此替换。
 * @author LiuJing
 *
 */
public abstract class CondimentDecorator extends Beverage {
	
	/***
	 * 用于保存要装饰的咖啡对象
	 */
	Beverage beverage;
	
	/***
	 * 所有子类都必须重新实现 getDescription()方法
	 */
	public abstract String getDescription();

}
Milk.java

package com.jing.decorator;

import java.text.DecimalFormat;

/***
 * 咖啡调料 牛奶  Milk >> CondimentDecorator >> Beverage
 * @author LiuJing
 *
 */
public class Milk extends CondimentDecorator {

	
	/**
	 * 构造时确定要加给谁
	 * @param beverage
	 */
	public Milk(Beverage beverage){
		this.beverage = beverage;
	}
	
	
	/***
	 * 不仅显示当前名,还说明被装饰的名字 
	 */
	@Override
	public String getDescription() {
		// TODO Auto-generated method stub
		return beverage.getDescription() + ", Milk";
	}

	/***
	 * 获取本身的价格和咖啡的价格
	 */
	@Override
	public double cost() {
		// TODO Auto-generated method stub
		double cost = beverage.cost();
		cost += 0.1;
		
		DecimalFormat df = new DecimalFormat("#.00");
		return Double.valueOf(df.format(cost));
	}

}

Mocha.java

package com.jing.decorator;

import java.text.DecimalFormat;

/***
 * 咖啡调料 摩卡 Mocha >> CondimentDecorator >> Beverage
 * 
 * @author LiuJing
 * 
 */
public class Mocha extends CondimentDecorator {


	/**
	 * 构造时确定要加给谁
	 * 
	 * @param beverage
	 */
	public Mocha(Beverage beverage) {
		this.beverage = beverage;
	}

	/***
	 * 不仅显示当前名,还说明被装饰的名字
	 */
	@Override
	public String getDescription() {
		// TODO Auto-generated method stub
		return beverage.getDescription() + ", Mocha";
	}

	/***
	 * 获取本身的价格和咖啡的价格
	 */
	@Override
	public double cost() {
		// TODO Auto-generated method stub
		double cost = beverage.cost();
		cost += 0.1;

		DecimalFormat df = new DecimalFormat("#.00");
		return Double.valueOf(df.format(cost));
	}

}
Soy.java

package com.jing.decorator;

import java.text.DecimalFormat;

/***
 * 咖啡调料 豆浆 Soy >> CondimentDecorator >> Beverage
 * 
 * @author LiuJing
 * 
 */
public class Soy extends CondimentDecorator {


	/**
	 * 构造时确定要加给谁
	 * 
	 * @param beverage
	 */
	public Soy(Beverage beverage) {
		this.beverage = beverage;
	}

	/***
	 * 不仅显示当前名,还说明被装饰的名字
	 */
	@Override
	public String getDescription() {
		// TODO Auto-generated method stub
		return beverage.getDescription() + ", Soy";
	}

	/***
	 * 获取本身的价格和咖啡的价格
	 */
	@Override
	public double cost() {
		// TODO Auto-generated method stub
		double cost = beverage.cost();
		cost += 0.1;

		DecimalFormat df = new DecimalFormat("#.00");
		return Double.valueOf(df.format(cost));
	}

}
Whip.java

package com.jing.decorator;

import java.text.DecimalFormat;

/***
 * 咖啡调料 奶泡 Whip >> CondimentDecorator >> Beverage
 * 
 * @author LiuJing
 * 
 */
public class Whip extends CondimentDecorator {


	/**
	 * 构造时确定要加给谁
	 * 
	 * @param beverage
	 */
	public Whip(Beverage beverage) {
		this.beverage = beverage;
	}

	/***
	 * 不仅显示当前名,还说明被装饰的名字
	 */
	@Override
	public String getDescription() {
		// TODO Auto-generated method stub
		return beverage.getDescription() + ", Whip";
	}

	/***
	 * 获取本身的价格和咖啡的价格
	 */
	@Override
	public double cost() {
		// TODO Auto-generated method stub
		double cost = beverage.cost();
		cost += 0.1;

		DecimalFormat df = new DecimalFormat("#.00");
		return Double.valueOf(df.format(cost));
	}

}
测试类

Test.java

package com.jing.decorator;

public class Test {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub

		
		// 点了一份浓咖啡
		Beverage beverage = new Espresso();
		System.out.println(
				beverage.getDescription() + ": $ " + beverage.cost());

		// 点了一份深烤
		Beverage beverage2 = new DarkRoast();
		beverage2 = new Mocha(beverage2);    // 加 摩卡装饰
		beverage2 = new Mocha(beverage2);    // 再加 摩卡装饰
		beverage2 = new Whip(beverage2);     // 再加 奶泡装饰
		System.out.println(
				beverage2.getDescription() + ": $ " + beverage2.cost());

		// 点了一份家庭混合
		Beverage beverage3 = new HouseBlend();
		beverage3 = new Soy(beverage3);   // 加 豆浆装饰
		beverage3 = new Mocha(beverage3); // 再加 摩卡装饰
		beverage3 = new Whip(beverage3);  // 再加 奶泡装饰
		System.out.println(
				beverage3.getDescription() + ": $ " + beverage3.cost());

		// 用 工厂模式 和 生成器模式,可以更好的建立被 装饰者对象
	}

}
输出:

Espresso: $ 12.0
DarkRoast, Mocha, Mocha, Whip: $ 10.3
HouseBlend, Soy, Mocha, Whip: $ 13.3

扩展:

现在咖啡要有区分 大,中,小杯的价格;

同理,

调料因为杯的大小也要区分不同的价格;

Beverage.java

package com.jing.decorator.size;

/**
 * 所有饮料的抽象超类
 * @author LiuJing
 *
 */
public abstract class Beverage {
	
	/**
	 * 杯子的容量类型
	 */
	public final static int TALL = 1;
	public final static int GRANDE = 2;
	public final static int VENTI = 3;
	
	/**
	 * 杯子的容量
	 */
	private int volume = 2; // 默认为中杯
	
	public int getSize() {
		return volume;
	}

	public void setSize(int valume) {
		this.volume = valume;
	}
	
	public String getSizeDescription() {
		
		String type = "未知杯的大小 ";
		switch(volume){
		case TALL:
			type = "小杯";
			break;
		case GRANDE:
			type = "中杯";
			break;
		case VENTI:
			type = "大杯";
			break;
		}

		return type;
	}

	/**
	 * 饮料的名字
	 */
	String description = "Unknow Beverage";
	

	
	/*
	 * 获取饮料的名字
	 */
	public String getDescription(){
		return description;
	}
	
	
	/***
	 * 获取饮料的价格
	 * @return 返回计算后的总价
	 */
	public abstract double cost();
	
}

DarkRoast.java

package com.jing.decorator.size;

import java.text.DecimalFormat;

/***
 * 深烤咖啡
 * @author LiuJing
 *
 */
public class DarkRoast extends Beverage {

	/***
	 * 构造时确定名字
	 */
	public DarkRoast(){
		description = "DarkRoast";
	}
	
	public DarkRoast(int volume){
		setSize(volume);
		description = "DarkRoast";
	}
	
	/***
	 * 价格
	 */
	public double cost() {
		double cost = 0.0;
		if (getSize() == Beverage.TALL) {
			cost = 10.0;
		}else if (getSize() == Beverage.GRANDE){
			cost = 15.0;
		}else if (getSize() == Beverage.VENTI){
			cost = 20.0;
		}
		
		DecimalFormat df = new DecimalFormat("#.00");
		return Double.valueOf(df.format(cost));
	}

}
Decaf.java

package com.jing.decorator.size;

import java.text.DecimalFormat;

/***
 * 低咖啡因咖啡
 * @author LiuJing
 *
 */
public class Decaf extends Beverage {

	/***
	 * 构造时确定名字
	 */
	public Decaf() {
		description = "Decaf";
	}
	public Decaf(int volume){
		setSize(volume);
		description = "Decaf";
	}
	
	/***
	 * 价格
	 */
	public double cost() {
		double cost = 0.0;
		if (getSize() == Beverage.TALL) {
			cost = 11.0;
		}else if (getSize() == Beverage.GRANDE){
			cost = 16.0;
		}else if (getSize() == Beverage.VENTI){
			cost = 21.0;
		}
		
		DecimalFormat df = new DecimalFormat("#.00");
		return Double.valueOf(df.format(cost));
	}

}
Espresso.java

package com.jing.decorator.size;

import java.text.DecimalFormat;

/***
 * 浓咖啡
 * @author LiuJing
 *
 */
public class Espresso extends Beverage {

	/***
	 *  构造时确定其名字
	 */
	public Espresso(){
		description = "Espresso";
	}
	
	public Espresso(int volume){
		setSize(volume);
		description = "Espresso";
	}
	
	/***
	 * 方法返回 该饮料的价格
	 */
	public double cost() {
		double cost = 0.0;
		if (getSize() == Beverage.TALL) {
			cost = 12.0;
		}else if (getSize() == Beverage.GRANDE){
			cost = 17.0;
		}else if (getSize() == Beverage.VENTI){
			cost = 22.0;
		}
		
		DecimalFormat df = new DecimalFormat("#.00");
		return Double.valueOf(df.format(cost));
	}
}

HouseBlend.java

package com.jing.decorator.size;

import java.text.DecimalFormat;

/***
 * 家庭混合咖啡
 * @author LiuJing
 *
 */
public class HouseBlend extends Beverage {

	/***
	 * 构造时确定其名字
	 */
	public HouseBlend(){
		description = "HouseBlend";
	}
	
	public HouseBlend(int volume){
		setSize(volume);
		description = "HouseBlend";
	}
	
	/***
	 * 返回该咖啡的价格
	 */
	public double cost() {
		double cost = 0.0;
		if (getSize() == Beverage.TALL) {
			cost = 13.0;
		}else if (getSize() == Beverage.GRANDE){
			cost = 18.0;
		}else if (getSize() == Beverage.VENTI){
			cost = 23.0;
		}
		
		DecimalFormat df = new DecimalFormat("#.00");
		return Double.valueOf(df.format(cost));
	}

}

CondimentDecorator.java

package com.jing.decorator.size;

/***
 * 调料装饰者抽象超类,继承至 Beverage,便于类型的统一,能彼此替换。
 * @author LiuJing
 *
 */
public abstract class CondimentDecorator extends Beverage {
	
	/***
	 * 要装饰的咖啡对象
	 */
	Beverage beverage;
	
	
	/***
	 * 所有子类都必须重新实现 getDescription()方法
	 */
	public abstract String getDescription();

}

Milk.java

package com.jing.decorator.size;

import java.text.DecimalFormat;

/***
 * 咖啡调料 牛奶  Milk >> CondimentDecorator >> Beverage
 * @author LiuJing
 *
 */
public class Milk extends CondimentDecorator {


	/**
	 * 构造时确定要加给谁
	 * @param beverage
	 */
	public Milk(Beverage beverage){
		this.beverage = beverage;
		setSize(beverage.getSize());
	}
	
	public int getSize(){
		return beverage.getSize();
	}
	
	/***
	 * 不仅显示当前名,还说明被装饰的名字 
	 */
	@Override
	public String getDescription() {
		// TODO Auto-generated method stub
		return beverage.getDescription() + ", Milk";
	}

	/***
	 * 获取本身的价格和咖啡的价格
	 */
	@Override
	public double cost() {
		// TODO Auto-generated method stub
		double cost = beverage.cost();
		if (getSize() == Beverage.TALL){
			cost += 0.1;
		} else if (getSize() == Beverage.GRANDE){
			cost += 0.2;
		} else if (getSize() == Beverage.VENTI){
			cost += 0.3;
		}
		
		DecimalFormat df = new DecimalFormat("#.00");
		return Double.valueOf(df.format(cost));
	}

}
Mocha.java

package com.jing.decorator.size;

import java.text.DecimalFormat;

/***
 * 咖啡调料 摩卡 Mocha >> CondimentDecorator >> Beverage
 * @author LiuJing
 *
 */
public class Mocha extends CondimentDecorator {

	
	/**
	 * 构造时确定要加给谁
	 * @param beverage
	 */
	public Mocha(Beverage beverage){
		this.beverage = beverage;
		setSize(beverage.getSize());
	}
	
	public int getSize(){
		return beverage.getSize();
	}
	
	/***
	 * 不仅显示当前名,还说明被装饰的名字 
	 */
	@Override
	public String getDescription() {
		// TODO Auto-generated method stub
		return beverage.getDescription() + ", Mocha";
	}

	/***
	 * 获取本身的价格和咖啡的价格
	 */
	@Override
	public double cost() {
		// TODO Auto-generated method stub
		double cost = beverage.cost();
		if (getSize() == Beverage.TALL){
			cost += 0.1;
		} else if (getSize() == Beverage.GRANDE){
			cost += 0.2;
		} else if (getSize() == Beverage.VENTI){
			cost += 0.3;
		}
		
		DecimalFormat df = new DecimalFormat("#.00");
		return Double.valueOf(df.format(cost));
	}

}

Soy.java

package com.jing.decorator.size;

import java.text.DecimalFormat;

/***
 * 咖啡调料 豆浆  Soy >> CondimentDecorator >> Beverage
 * @author LiuJing
 *
 */
public class Soy extends CondimentDecorator {

	
	/**
	 * 构造时确定要加给谁
	 * @param beverage
	 */
	public Soy(Beverage beverage){
		this.beverage = beverage;
		setSize(beverage.getSize());
	}
	
	public int getSize(){
		return beverage.getSize();
	}
	
	/***
	 * 不仅显示当前名,还说明被装饰的名字 
	 */
	@Override
	public String getDescription() {
		// TODO Auto-generated method stub
		return beverage.getDescription() + ", Soy";
	}

	/***
	 * 获取本身的价格和咖啡的价格
	 */
	@Override
	public double cost() {
		// TODO Auto-generated method stub
		double cost = beverage.cost();
		if (getSize() == Beverage.TALL){
			cost += 0.1;
		} else if (getSize() == Beverage.GRANDE){
			cost += 0.2;
		} else if (getSize() == Beverage.VENTI){
			cost += 0.3;
		}
		
		DecimalFormat df = new DecimalFormat("#.00");
		return Double.valueOf(df.format(cost));
	}

}
Whip.java

package com.jing.decorator.size;

import java.text.DecimalFormat;

/***
 * 咖啡调料 奶泡  Whip >> CondimentDecorator >> Beverage
 * @author LiuJing
 *
 */
public class Whip extends CondimentDecorator {


	/**
	 * 构造时确定要加给谁
	 * @param beverage
	 */
	public Whip(Beverage beverage){
		this.beverage = beverage;
		setSize(beverage.getSize());
	}
	
	public int getSize(){
		return beverage.getSize();
	}
	
	/***
	 * 不仅显示当前名,还说明被装饰的名字 
	 */
	@Override
	public String getDescription() {
		// TODO Auto-generated method stub
		return beverage.getDescription() + ", Whip";
	}

	/***
	 * 获取本身的价格和咖啡的价格
	 */
	@Override
	public double cost() {
		// TODO Auto-generated method stub
		double cost = beverage.cost();
		if (getSize() == Beverage.TALL){
			cost += 0.1;
		} else if (getSize() == Beverage.GRANDE){
			cost += 0.2;
		} else if (getSize() == Beverage.VENTI){
			cost += 0.3;
		}
		
		DecimalFormat df = new DecimalFormat("#.00");
		return Double.valueOf(df.format(cost));
	}

}
Test.java

package com.jing.decorator.size;

public class Test {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub

		
		// 点了一份浓咖啡
		Beverage beverage = new Espresso(); // 使用默认中杯
		System.out.println(beverage.getSizeDescription()
				+ beverage.getDescription() + ": $ " + beverage.cost());

		// 点了一份深烤
		Beverage beverage2 = new DarkRoast();// 默认为中号
		beverage2.setSize(Beverage.TALL);    // 重新设置杯号为小号
		beverage2 = new Mocha(beverage2);    // 加 摩卡装饰
		beverage2 = new Mocha(beverage2);    // 再加 摩卡装饰
		beverage2 = new Whip(beverage2);     // 再加 奶泡装饰
		System.out.println(beverage2.getSizeDescription()
				+ beverage2.getDescription() + ": $ " + beverage2.cost());

		// 点了一份家庭混合
		Beverage beverage3 = new HouseBlend(Beverage.VENTI); // 构造初始时就设为大号
		beverage3 = new Soy(beverage3);   // 加 豆浆装饰
		beverage3 = new Mocha(beverage3); // 再加 摩卡装饰
		beverage3 = new Whip(beverage3);  // 再加 奶泡装饰
		System.out.println(beverage3.getSizeDescription()
				+ beverage3.getDescription()
				+ ": $ " + beverage3.cost());

		// 用 工厂模式 和 生成器模式,可以更好的建立被 装饰者对象
	}

}

输出:

中杯Espresso: $ 17.0
小杯DarkRoast, Mocha, Mocha, Whip: $ 10.3
大杯HouseBlend, Soy, Mocha, Whip: $ 23.9

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值