装饰者模式学习:模拟咖啡馆的点单系统来剖析装饰者模式的使用 + 装饰者模式在java I/O 中的应用
通过模拟咖啡馆的点单系统来剖析装饰者模式的使用
参考:https://blog.csdn.net/gududedabai/article/details/81989196
一)、传统的点单系统构建,每一个种类的咖啡都定义一个类
- 弊端:
- 如果为每一种混合咖啡都定义一个类,那么,会产生很多的类对象。
混合咖啡的价格是在单品咖啡的基础上的,如若某一单品咖啡的价格
/**
发生改变,那么就要修改与之关联的所有的混合咖啡的价格。- 抛转引玉
- --:咖啡订单系统
1.咖啡店售卖四款基础咖啡
Espressio, ShortBlack, LongBlack, Decaf
2.可以在四款基础咖啡的基础上加入调料,例如Milk,Soy、Chocolate ,组成混合咖啡
*/3.每款咖啡都共同的description属性,和getDecription(),指料明加入的调料,cost()计算咖啡所需的价格。
- 咖啡的共同属性
*/
public abstract class Coffee {
/**- 描述咖啡加入的调料+单品种类
*/
private String description;
public Coffee() {
}public void setDescription(String description) {
this.description = description;
}public Coffee(String description) {
/**
this.description = description;
}- 打印购买的咖啡信息
*/
void getDescription(){
System.out.println(description);
}
- 计算咖啡所需花费的价格
- @return
*/
public abstract int CoffeePrice();
}
- 描述咖啡加入的调料+单品种类
单品咖啡:
/**
* 低糖咖啡
*/
public class Decaf extends Coffee {
Decaf(String description){
super(description);
}
@Override
public int CoffeePrice() {
return 30;
}
}
单品咖啡:
/**
* 浓咖啡
*/
public class Espressio extends Coffee {
Espressio(String description){
super(description);
}
@Override
public int CoffeePrice() {
return 10;
}
}
单品咖啡:
/**
* 黑咖啡
*/
public class LongBlack extends Coffee{
LongBlack(String description){
super(description);
}
@Override
public int CoffeePrice() {
return 20;
}
}
单品咖啡:
/**
* 浓缩咖啡
*/
public class ShortBlack extends Coffee{
ShortBlack(String description){
super(description);
}
@Override
public int CoffeePrice() {
return 15;
}
}
混合咖啡:
/**
* 组合咖啡: 无糖+牛奶
*/
public class DecafAndMilk extends Coffee{
DecafAndMilk(String description){
super(description);
}
@Override
public int CoffeePrice() {
return 35;
}
}
售卖咖啡:
1)、在new 对象时就指名加入的调料,getDescription()时打印咖啡种类和加
入的调料,直接调用CoffeePrice()返回咖啡的价格。
2)、需要为每一种混合咖啡都创建一个咖啡对象
/**
* 售卖咖啡
* 使用传统方式来构建售卖咖啡的类:
* --: 所有的混合咖啡都实现了超类Coffee
* 此时,出现了一个问题
* ---》1.因为调料的种类很多,调料与调料之间的组合方式也很多,这时咖啡类的数量就会增多。
* 2.因为所有的混合咖啡都是在单品咖啡的基础上构建的,当单品咖啡的价格发生了调整,所有
* 与单品咖啡相关的混合咖啡的价格都要进行调整。
*/
public class SaleCoffee {
public static void main(String[] args) {
Coffee coffee = new Decaf("无糖咖啡");
//打印咖啡的种类和价格
coffee.getDescription();
System.out.println(coffee.CoffeePrice());
//无糖+牛奶的咖啡
Coffee coffee1 = new DecafAndMilk("无糖咖啡:+牛奶");
coffee1.getDescription();
System.out.println(coffee1.CoffeePrice());
}
}
结果:
无糖咖啡
30
无糖咖啡:+牛奶
35
二)、将调料声明在超类中,在单品咖啡的基础上加入调料,只需定义单品咖啡类即可
- 好处
1.减少了组合咖啡类的定义,通过判断hasXxx()可以在四个单品咖啡的 基础上加调料,即可以通过四个单品类来得到很多的组合咖啡 - 弊端
- 当需要加入一种调料时,需要修改超类中的代码,这样违反了代码的开闭原则, 一旦修改了代码就会有产生bug的风险。
- 当用户需要加两份调料,如:加入两份牛奶时不能通过hasMilk()来计算咖啡的价格。
所有咖啡的超类:
将所有的调料以boolean的形式声明在超类中,并通过hasXxx()来判断是否加
入调料以及计算咖啡的价格。
import com.sun.xml.internal.ws.util.StringUtils;
/**
* 构建第二种形式的咖啡超类
* --: 一开始就给定了咖啡的调料
*/
public abstract class Coffee {
/**
* 描述咖啡的种类和调料
*/
private String description;
/**
* 将咖啡的调料内置在超类中
*/
private boolean milk;
private boolean soy;
private boolean chocolate;
public Coffee(String description) {
this.description = description;
}
public Coffee() {
}
/**
* 判断是否加了牛奶
* @return
*/
public Boolean hashMilk(){
return milk;
}
/**
* 判断是否加了豆浆
* @return
*/
public Boolean hashSoy(){
return soy;
}
/**
* 判断是否加了巧克力
* @return
*/
public Boolean hashChocolate(){
return chocolate;
}
/**
* 根据咖啡的种类和调料计算咖啡的价格
*/
public abstract int CoffeePrice();
public void getDescription() {
System.out.println(description);
}
public void setDescription(String description) {
this.description += description;
}
public void setMilk(boolean milk) {
this.milk = milk;
if(milk == true) {
setDescription("+牛奶");
}
}
public void setSoy(boolean soy){
this.soy = soy;
if(soy == true){
setDescription("+豆浆");
}
}
public void setChocolate(boolean chocolate) {
this.chocolate = chocolate;
if(chocolate == true) {
setDescription("+chocolate");
}
}
}
单品咖啡:
通过hasXxx()来计算最终的价格。
**
* 低糖咖啡
*/
public class Decaf extends Coffee {
Decaf(String description){
super(description);
}
@Override
public int CoffeePrice() {
//单品低糖咖啡的价格为30
int cost = 30;
if(this.hashMilk()){
cost = cost + 5;
}
if(this.hashChocolate()){
cost = cost + 10;
}
if(this.hashSoy()){
cost = cost + 2;
}
return cost;
}
}
单品咖啡:
/**
* 浓咖啡
*/
public class Espressio extends Coffee {
Espressio(String description){
super(description);
}
@Override
public int CoffeePrice() {
//单品浓咖啡的价格为10
int cost = 10;
if(this.hashMilk()){
cost = cost + 5;
}
if(this.hashChocolate()){
cost = cost + 10;
}
if(this.hashSoy()){
cost = cost + 2;
}
return cost;
}
}
单品咖啡:
/**
* 黑咖啡
*/
public class LongBlack extends Coffee{
LongBlack(String description){
super(description);
}
/**
* 判断当前种类的咖啡是否有加入调料,若有则加入调料的价格
* @return
*/
@Override
public int CoffeePrice() {
//单品黑咖啡的价格为20
int cost = 20;
if(this.hashMilk()){
cost = cost + 5;
}
if(this.hashChocolate()){
cost = cost + 10;
}
if(this.hashSoy()){
cost = cost + 2;
}
return cost;
}
}
单品咖啡:
/**
* 浓缩咖啡
*/
public class ShortBlack extends Coffee{
ShortBlack(String description){
super(description);
}
@Override
public int CoffeePrice() {
//单品浓缩咖啡的价格为15
int cost = 15;
if(this.hashMilk()){
cost = cost + 5;
}
if(this.hashChocolate()){
cost = cost + 10;
}
if(this.hashSoy()){
cost = cost + 2;
}
return cost;
}
}
售卖咖啡:
/**
* 将所有的调料放在超类中
* --:减少了组合咖啡类的定义,通过判断hasXxx()可以在四个单品咖啡的基础上加调料,即可以通过四个单品类来得到很多的组合咖啡
*
* 弊端:
* --:1.当需要加入一种调料时,需要修改超类中的代码,这样违反了代码的开闭原则,一旦修改了代码就会有产生bug的风险。
* 2.当用户需要加两份调料,如:加入两份牛奶时不能通过hasMilk()来计算咖啡的价格。
*
*/
public class SaleCoffee {
public static void main(String[] args) {
//先选择咖啡单品
Coffee coffee = new Decaf("无糖咖啡:");
//组合咖啡: 无糖 + 牛奶
coffee.setMilk(true);
coffee.setChocolate(false);
coffee.getDescription();
System.out.println(coffee.CoffeePrice());
}
}
结果:
无糖咖啡:+牛奶
35
三)你要喝什么味的咖啡?
使用装饰者模式来实现不同咖啡种类的搭配:
(装饰者模式的模型)
主体接口类:
被装饰类:
装饰类:
具体装饰类1;具体装饰类2;具体装饰类3;
具体装饰类具有叠加效果
主体接口类:
/**
* 实现装饰者模式的逻辑:
* 1.公共接口类:
* --:需要一个公共接口类
* 该接口定义了被装饰类的主要逻辑方法,被装饰者和装饰者分别去实现或继承这个接口
* 2.被装饰类:
* 被装饰者实现公共接口类,并对接口方法做具体实现
* 3.装饰类:
* 装饰类接收被装饰类对象,调用被装饰类的方法
* 4.具体装饰类
* 继承装饰类,做具体的装饰逻辑实现
*/
/**
* 公共接口类:
* --: 定义需要装饰的接口方法以及公共的对象属性
*/
public abstract class Coffee {
private String description;
private double price;
/**
* 获取咖啡的价格
* @return
*/
public abstract double getCoffeePrice();
public abstract String getCoffeeDescription();
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
}
被装饰类:
/**
* 被装饰类:
* --:该类为咖啡单品类,可以为该咖啡加各种调料,然后计算咖啡的价格以及获取咖啡的种类和所加的调料
*/
public class Decaf extends Coffee{
/**
* 因为是单品咖啡,所以对象刚创建时价格和种类就已经确定了
*/
Decaf(){
setPrice(30);
setDescription("低糖:");
}
@Override
public double getCoffeePrice() {
return this.getPrice();
}
@Override
public String getCoffeeDescription() {
return this.getDescription();
}
}
装饰类:
/**
* 装饰类:
* --:接收被装饰对象,调用被装饰对象的方法
*/
public class Decorator extends Coffee{
/**
* 被装饰对象
*/
private Coffee coffee = null;
/**
* 一初始化就有传入一个被装饰对象
*/
Decorator(Coffee coffee){
this.coffee = coffee;
}
@Override
public double getCoffeePrice() {
//使用被装饰者的功能
return coffee.getCoffeePrice();
}
@Override
public String getCoffeeDescription(){
return coffee.getCoffeeDescription();
}
}
具体装饰类:
/**
* 具体的装饰者实现类
* --:继承装饰类
*/
public class Milk extends Decorator{
Milk(Coffee coffee) {
super(coffee);
setPrice(10);
setDescription("+牛奶");
}
@Override
public double getCoffeePrice() {
//使用被装饰者的功能
return super.getCoffeePrice()+this.getPrice();
}
@Override
public String getCoffeeDescription(){
return super.getCoffeeDescription()+this.getDescription();
}
}
具体装饰类:
public class Chocolate extends Decorator {
Chocolate(Coffee coffee) {
super(coffee);
setPrice(15);
setDescription("+chocolate");
}
@Override
public double getCoffeePrice() {
//使用被装饰者的功能
return super.getCoffeePrice()+this.getPrice();
}
@Override
public String getCoffeeDescription(){
return super.getCoffeeDescription()+this.getDescription();
}
}
生产咖啡:
public class SaleCoffee {
public static void main(String[] args) {
//加双份牛奶
Decorator decorator = new Decorator(new Milk(new Milk(new Decaf())));
System.out.println(decorator.getCoffeeDescription());
System.out.println(decorator.getCoffeePrice());
}
}
结果:
低糖:+牛奶+牛奶
50.0
四)、装饰者模式举例二、你要吃什么味道的鸡腿堡?
参考: https://blog.csdn.net/jason0539/article/details/22713711
主题接口类:
/**
* 使用汉堡店买汉堡的例子来实现装饰者模式
* --:汉堡可以选择加生菜、火腿、沙拉、番茄酱
*/
public abstract class Hamburger {
/**
* 汉堡名字
*/
private String name;
/**
* 汉堡的价格
*/
private double price;
/**
* 制作汉堡
*/
public abstract String product();
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
}
被装饰类:
/**
* 基础汉堡
*/
public class ChickenBurger extends Hamburger{
ChickenBurger(){
setName("鸡腿堡");
setPrice(25);
}
@Override
public String product() {
return this.getName()+": " + this.getPrice();
}
}
装饰类:
public class Decorator extends Hamburger {
Hamburger hamburger;
Decorator(Hamburger hamburger){
this.hamburger = hamburger;
}
public Decorator() {
}
@Override
public String product() {
return hamburger.product();
}
}
具体装饰类:
/**
* 装饰类,给汉堡加生菜
*/
public class Lettuce extends Decorator{
Lettuce(Hamburger hamburger) {
this.hamburger = hamburger;
setName(hamburger.getName()+"+生菜");
setPrice(hamburger.getPrice()+2);
}
@Override
public String product(){
return getName()+": "+getPrice();
}
}
具体装饰类:
/**
* 给火腿加鸡蛋
*/
public class Age extends Decorator{
Age(Hamburger hamburger) {
this.hamburger = hamburger;
setName(hamburger.getName()+"+鸡蛋");
setPrice(hamburger.getPrice()+1.5);
}
@Override
public String product() {
return getName()+": "+getPrice();
}
}
制作汉堡:
public class SaleHamburger {
public static void main(String[] args) {
//鸡腿堡 +生菜 + 鸡蛋
Decorator decorator = new Decorator(new Lettuce(new Age(new ChickenBurger())));
//原味鸡腿堡
Decorator decorator1 = new Decorator(new ChickenBurger());
//鸡腿堡 +生菜
Decorator decorator2 = new Decorator(new Lettuce(new ChickenBurger()));
//鸡腿堡 +鸡蛋
Decorator decorator3 = new Decorator(new Age(new ChickenBurger()));
System.out.println(decorator.product());
System.out.println(decorator1.product());
System.out.println(decorator2.product());
System.out.println(decorator3.product());
}
}
结果:
鸡腿堡+鸡蛋+生菜: 28.5
鸡腿堡: 25.0
鸡腿堡+生菜: 27.0
鸡腿堡+鸡蛋: 26.5
五)、装饰者模式在java Jdk中的应用
装饰者模式应用在java Jdk api文档中的IO流机制
I) :主题接口类:InputStream
II):被装饰类:
FileInputStream; StringBufferInputStream, ByteArrayInputStream
装饰类:
FilterInputStream
III): 具体实现类:
BufferInputStream; DataInputStream; LineNumberInputStream
使用jdk设计好的装饰者模式,将流对象中的小写字母转为大写字母
具体装饰类:
继承FilterInpustream
/**
* java的Io流机制就使用了装饰模式
* InputStream: 接口类
* FileInputStream: StringBufferInputStream: ByteArrayInPutStream : 被装饰类
* FilterInputStream: 装饰类接口
* --: 具体装饰类
* BufferInputStream: DataInputStream: LineNumberInputStream
*
*/
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* 实现一个将流中的小写字母转为大写字母的装饰者对象
*/
public class UpperCaseInputStream extends FilterInputStream {
protected UpperCaseInputStream(InputStream in) {
super(in);
}
@Override
//读取流,将流中的小写字母转为大写字母
public int read() throws IOException//单字符的读
{
int c=super.read();//这个super.read()就是调用上面super(in);的主题对象
return c==-1?c:Character.toUpperCase((char)(c));
}
@Override
public int read(byte[] b,int offset,int len) throws IOException//多字符的读
{
int result=super.read(b,offset,len);
for(int i=0;i<result;i++)
{
b[i]=(byte)Character.toUpperCase((char)(b[i]));
}
return result;
}
}
使用类:
public class Test {
public static void main(String[] args) {
int c;
try {
InputStream in = new UpperCaseInputStream(new BufferedInputStream(
new FileInputStream("F:\\test.txt")));
while((c=in.read())>=0)
{
System.out.print((char)c);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
原文地址:https://www.cnblogs.com/Auge/p/11589342.html
- 混淆漏洞CVE-2017-0213技术分析
- android service 学习(上)
- 黑帽SEO剖析之隐身篇
- Java中如何判断一个字符串是Java代码还是英文呢?
- 将复杂查询写到SQL配置文件--SOD框架的SQL-MAP技术简介
- Java中实现找到两个数组交集的2种方法,开发实用
- Java Web中JSP的include 指令知识点总结——每日一语法学习
- Java反序列化漏洞从理解到实践
- ORM查询语言(OQL)简介--高级篇(续):庐山真貌
- Java中使用Hibernate系列之映射关联启动工作学习(第五节)
- Java中使用Hibernate系列之单向Set-based的关联学习(第四节)
- Java中使用Hibernate系列之加载并存储对象学习(第三节)
- Java中使用Hibernate系列之启动方法学习(第二节)
- Java中使用Hibernate系列之映射文件学习(第一节)
- java教程
- Java快速入门
- Java 开发环境配置
- Java基本语法
- Java 对象和类
- Java 基本数据类型
- Java 变量类型
- Java 修饰符
- Java 运算符
- Java 循环结构
- Java 分支结构
- Java Number类
- Java Character类
- Java String类
- Java StringBuffer和StringBuilder类
- Java 数组
- Java 日期时间
- Java 正则表达式
- Java 方法
- Java 流(Stream)、文件(File)和IO
- Java 异常处理
- Java 继承
- Java 重写(Override)与重载(Overload)
- Java 多态
- Java 抽象类
- Java 封装
- Java 接口
- Java 包(package)
- Java 数据结构
- Java 集合框架
- Java 泛型
- Java 序列化
- Java 网络编程
- Java 发送邮件
- Java 多线程编程
- Java Applet基础
- Java 文档注释
- [踩坑]STM32外部8M晶体不起振会有什么现象?
- 干掉 GuavaCache:Caffeine 才是本地缓存的王
- 美团数据怎么爬,看看这个文章吧!
- [硬件]关于SPI Flash那些你不知道的事儿
- 「新特性」Spring Boot 全局懒加载机制了解一下
- 做olap一定要要了解的Druid存储结构
- 【ST开发板评测】使用Python来开发STM32F411
- 一文详解 Ansible 自动化运维!
- 手把手教你搭建织女星开发板RISC-V开发环境
- R语言多臂试验 - 我们应该考虑多重性吗?
- Stata估算观测数据的风险比
- R语言利用基线协变量提高随机对照试验的效率
- R语言使用倾向评分提高RCT(随机对照试验)的效率
- R语言调整随机对照试验中的基线协变量
- R语言用多重插补法估算相对风险