java实现咖啡店模拟_装饰者模式学习:模拟咖啡馆的点单系统来剖析装饰者模式的使用 + 装饰者模式在java I/O 中的应用...

通过模拟咖啡馆的点单系统来剖析装饰者模式的使用

一)、传统的点单系统构建,每一个种类的咖啡都定义一个类

弊端:

如果为每一种混合咖啡都定义一个类,那么,会产生很多的类对象。

混合咖啡的价格是在单品咖啡的基础上的,如若某一单品咖啡的价格

发生改变,那么就要修改与之关联的所有的混合咖啡的价格。

咖啡的共同属性:

/**

* 抛转引玉

* --:咖啡订单系统

* 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

四)、装饰者模式举例二、你要吃什么味道的鸡腿堡?

主题接口类:

/**

* 使用汉堡店买汉堡的例子来实现装饰者模式

* --:汉堡可以选择加生菜、火腿、沙拉、番茄酱

*/

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

{

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();

}

}

}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值