绍兴金圣建设有限公司网站,制作网站赚钱,系部网站建设需求分析运行需求,代理服务器在哪里找生活案例
咖啡厅 咖啡定制案例
在咖啡厅中#xff0c;有多种不同类型的咖啡#xff0c;客户在预定了咖啡之后#xff0c;还可以选择添加不同的调料来调整咖啡的口味#xff0c;当客户点了咖啡添加了不同的调料#xff0c;咖啡的价格需要做出相应的改变。
要求#xff…生活案例
咖啡厅 咖啡定制案例
在咖啡厅中有多种不同类型的咖啡客户在预定了咖啡之后还可以选择添加不同的调料来调整咖啡的口味当客户点了咖啡添加了不同的调料咖啡的价格需要做出相应的改变。
要求程序实现具有良好的拓展性、改动方便、维护方便
【方案一】
写一个抽象类然后将所有咖啡和调料组合形成多个类来继承抽象类缺点当增加一个单品咖啡或者调味类的数量就会大增产生类爆炸问题
【方案二】 分析
可以控制类的数量不至于造成很多的类增加或者删除调料种类时代码的维护量很大如果同样一种调料可以点多份时可以将 hasMilk 返回一个对应int类型的数据来表示调料的份数
装饰者模式介绍
介绍
动态的将新功能附加到对象上。在对象功能扩展方面它比继承更好装饰者模式也体现了开闭原则(ocp)假如有一块蛋糕如果加上奶油就变成了奶油蛋糕再加上草莓就变成了草莓奶油蛋糕。整个过程就是在不断装饰蛋糕的过程。根据装饰者模式编写的程序的对象与蛋糕十分相似。首先有一个相当于蛋糕的对象然后像不断地装饰蛋糕一样地不断地对其增加功能它就变成了使用目的更加明确的对象。
出场角色
Component主体被装饰对象增加功能时的核心角色定义了接口APIConcreteComponent具体主体实现了Component角色所定义的接口Decorator装饰者该角色具有与Component角色相同的接口(API)在它内部保存了Component角色ConcreteDecorator( 具体的装饰者) 案例实现
案例一咖啡厅问题
类图 代码实现
【被装饰主体】
package com.test.decorator;public abstract class Drink {/**
* 描述
*/
public String des;
/**
* 价格
*/
private float price 0.0f;
public String getDes() {
return des;
}
public void setDes(String des) {
this.des des;
}
public float getPrice() {
return price;
}
public void setPrice(float price) {
this.price price;
}/**
* 计算费用的抽象方法需要子类来实现
* return
*/
public abstract float cost();}【缓冲类整合所有咖啡的共同点这个类不一定要写要结合实际情况】
package com.test.decorator;public class Coffee extends Drink {Override
public float cost() {
return super.getPrice();
}}【单品咖啡意大利咖啡】
package com.test.decorator;public class Espresso extends Coffee {public Espresso() {
setDes( 意大利咖啡 );
// 初始化意大利咖啡的价格
setPrice(6.0f);
}
}【单品咖啡美式咖啡】
package com.test.decorator;public class LongBlack extends Coffee {public LongBlack() {
setDes( longblack );
setPrice(5.0f);
}
}【单品咖啡浓咖啡】
package com.test.decorator;public class ShortBlack extends Coffee{public ShortBlack() {
setDes( shortblack );
setPrice(4.0f);
}
}【装饰者】
package com.test.decorator;/**
* 装饰物继承了Drink还聚合了Drink
*/
public class Decorator extends Drink {
private Drink obj;/**
* 聚合Drink
* param obj
*/
public Decorator(Drink obj) {
this.obj obj;
}Override
public float cost() {
// getPrice 自己价格 咖啡的价格
return super.getPrice() obj.cost();
}/**
* 输出信息
* return
*/
Override
public String getDes() {
// obj.getDes() 输出被装饰者的信息
return des getPrice() obj.getDes();
}}【具体装饰者巧克力】
package com.test.decorator;/**
* 具体的Decorator 这里就是调味品
*/
public class Chocolate extends Decorator {public Chocolate(Drink obj) {
super(obj);
setDes( 巧克力 );
// 调味品 的价格
setPrice(3.0f);
}}【具体装饰者牛奶】
package com.test.decorator;public class Milk extends Decorator {public Milk(Drink obj) {
super(obj);
setDes( 牛奶 );
setPrice(2.0f);
}}【具体装饰者豆浆】
package com.test.decorator;public class Soy extends Decorator{public Soy(Drink obj) {
super(obj);
setDes( 豆浆 );
setPrice(1.5f);
}}【主类】
package com.test.decorator;public class CoffeeBar {public static void main(String[] args) {System.out.println( 订单1 );
// 装饰者模式下的订单2份巧克力一份牛奶的LongBlack
// 1. 点一份 LongBlack
Drink order new LongBlack();
System.out.println(费用 order.cost());
System.out.println(描述 order.getDes());
System.out.println();// 2.加入一份牛奶
order new Milk(order);
System.out.println(order 加入一份牛奶 费用 order.cost());
System.out.println(order 加入一份牛奶 描述 order.getDes());
System.out.println();// 3.加入一份巧克力
order new Chocolate(order);
System.out.println(order 加入一份牛奶 加入一份巧克力 费用 order.cost());
System.out.println(order 加入一份牛奶 加入一份巧克力 描述 order.getDes());
System.out.println();// 4.加入一份巧克力
order new Chocolate(order);
System.out.println(order 加入一份牛奶 加入2份巧克力 费用 order.cost());
System.out.println(order 加入一份牛奶 加入2份巧克力 描述 order.getDes());
System.out.println();}}【运行】 订单1
费用5.0
描述 longblack order 加入一份牛奶 费用 7.0
order 加入一份牛奶 描述 牛奶 2.0 longblack order 加入一份牛奶 加入一份巧克力 费用 10.0
order 加入一份牛奶 加入一份巧克力 描述 巧克力 3.0 牛奶 2.0 longblack order 加入一份牛奶 加入2份巧克力 费用 13.0
order 加入一份牛奶 加入2份巧克力 描述 巧克力 3.0 巧克力 3.0 牛奶 2.0 longblack 咖啡样式拓展代码实现
只需要新增一个单品咖啡类就可以购买了拓展性非常强大
【新增单品咖啡无因咖啡】
package com.test.decorator;public class DeCaf extends Coffee {public DeCaf() {
setDes( 无因咖啡 );
setPrice(1.0f);
}
}【主类】
package com.test.decorator;public class CoffeeBar {public static void main(String[] args) {System.out.println( 订单2 );Drink order2 new DeCaf();
System.out.println(order2 无因咖啡 费用 order2.cost());
System.out.println(order2 无因咖啡 描述 order2.getDes());
System.out.println();order2 new Milk(order2);
System.out.println(order2 无因咖啡 加入一份牛奶 费用 order2.cost());
System.out.println(order2 无因咖啡 加入一份牛奶 描述 order2.getDes());
System.out.println();}}【运行】 订单2
order2 无因咖啡 费用 1.0
order2 无因咖啡 描述 无因咖啡 order2 无因咖啡 加入一份牛奶 费用 3.0
order2 无因咖啡 加入一份牛奶 描述 牛奶 2.0 无因咖啡 Process finished with exit code 0案例二
类图 代码实现
【抽象主体】
package com.test.decorator.Sample;public abstract class Display {
/**
* 获取横向字符数抽象方法需要子类去实现
* return
*/
public abstract int getColumns();/**
* 获取纵向行数抽象方法需要子类去实现
* return
*/
public abstract int getRows();/**
* 获取第row行的字符串抽象方法需要子类去实现
* param row
* return
*/
public abstract String getRowText(int row);/**
* 显示所有行的字符串
*/
public void show() {
// 遍历行数
for (int i 0; i getRows(); i) {
// 获取改行的字符串来打印出来
System.out.println(getRowText(i));
}
}
}【具体主体】
package com.test.decorator.Sample;/**
* 该类用来显示单行字符串
*/
public class StringDisplay extends Display {
/**
* 要显示的字符串
*/
private String string;/**
* 构造方法
*
* param string 要显示的字符串
*/
public StringDisplay(String string) {
this.string string;
}Override
public int getColumns() {
// 字符数
return string.getBytes().length;
}Override
public int getRows() {
// 行数是1
return 1;
}/**
* 只有第0行可以获取到字符串其他都是空
* param row
* return
*/
Override
public String getRowText(int row) {
// 仅当row为0时返回值
if (row 0) {
return string;
} else {
return null;
}
}
}【抽象装饰者】
package com.test.decorator.Sample;/**
* 装饰者抽象类注意要继承抽象主体并聚合抽象主体
*/
public abstract class Border extends Display {
/**
* 表示被装饰物
*/
protected Display display;protected Border(Display display) {
// 在生成实例时通过参数指定被装饰物
this.display display;
}
}【具体修饰者1】
package com.test.decorator.Sample;/**
* 在字符串的左右两侧添加边框
*/
public class SideBorder extends Border {
/**
* 表示装饰边框的字符
*/
private char borderChar;/**
* 通过构造函数指定Display和装饰边框字符
* param display
* param ch
*/
public SideBorder(Display display, char ch) {
super(display);
this.borderChar ch;
}/**
* 字符数为字符串字符数加上两侧边框字符数
* return
*/
public int getColumns() {
return 1 display.getColumns() 1;
}/**
* 行数即被装饰物的行数
* return
*/
public int getRows() {
// 在字符串的两侧添加字符并不会增加行数所以直接返回主体的行数即可
return display.getRows();
}/**
* 指定的那一行的字符串为被装饰物的字符串加上两侧的边框的字符
* param row
* return
*/
public String getRowText(int row) {
return borderChar display.getRowText(row) borderChar;
}
}【具体装饰者2】
package com.test.decorator.Sample;/**
* 在字符串的上下左右都加上装饰框
*/
public class FullBorder extends Border {
public FullBorder(Display display) {
super(display);
}public int getColumns() {
// 字符数为被装饰物的字符数加上两侧边框字符数
return 1 display.getColumns() 1;
}public int getRows() {
// 行数为被装饰物的行数加上上下边框的行数
return 1 display.getRows() 1;
}/**
* 指定的那一行的字符串
*
* param row
* return
*/
public String getRowText(int row) {
if (row 0) { // 上边框
return makeLine(-, display.getColumns()) ;
} else if (row display.getRows() 1) { // 下边框
return makeLine(-, display.getColumns()) ;
} else { // 其他边框
return | display.getRowText(row - 1) |;
}
}/**
* 生成一个重复count次字符ch的字符串
*
* param ch
* param count
* return
*/
private String makeLine(char ch, int count) {
StringBuffer buf new StringBuffer();
for (int i 0; i count; i) {
buf.append(ch);
}
return buf.toString();
}
}【主类】
package com.test.decorator.Sample;public class Main {
public static void main(String[] args) {
Display b1 new StringDisplay(Hello, world.);
Display b2 new SideBorder(b1, #);
Display b3 new FullBorder(b2);
b1.show();
b2.show();
b3.show();
Display b4
new SideBorder(new FullBorder(new FullBorder(new SideBorder(new FullBorder(new StringDisplay(你好世界。)),*))),/
);
b4.show();
}
}【运行】
Hello, world.#Hello, world.#---------------
|#Hello, world.#|
---------------/------------------------/
/|----------------------|/
/||*------------------*||/
/||*|你好世界。|*||/
/||*------------------*||/
/|----------------------|/
/------------------------/Process finished with exit code 0装饰着模式在IO流源码的应用 InputStream 是抽象类, 类似我们前面讲的 DrinkFileInputStream 是 InputStream 子类类似我们前面的 DeCaf, LongBlackFilterInputStream 是 InputStream 子类类似我们前面 的 Decorator 修饰者DataInputStream 是 FilterInputStream 子类具体的修饰者类似前面的 Milk, Soy 等FilterInputStream 类 有 protected volatile InputStream in; 即聚合了被装饰者 总结
在装饰者模式中装饰者与被装饰者具有一致性。装饰者类是表示被装饰者的类的子类这就体现了它们之间的一致性它们具有相同的接口这样就算被装饰者被装饰了接口还是向外暴露的接口的透明性可以在不改变被装饰者的前提下增加功能如案例中在显示字符串之前对字符串进行修饰只需要一些装饰物即可添加许多功能通过自由组合调料可以让咖啡拥有各种不同的味道装饰者模式也有缺点会导致程序中增加许多功能类似的很小的类
什么是父类和子类的一致性 可以将子类的实例保存到父类的变量中也可以直接调用从父类中继承的方法
如何让自己和被委托对象有一致性
使用委托让接口具有透明性时自己和被委托对象具有一致性 Rose和Violet都有相同的method方法。Rose将method方法的处理委托给了 Violet。这两个类虽然都有 method 方法但是没有明确在代码中体现出“共通性”。
如果要明确地表示method方法是共通的只需要像下面这样编写一个抽象类Flower然后让Rose和Violet都继承并实现方法即可。 或者让Flower作为接口