1. 装饰者模式
小王家里新开了一家咖啡店,咖啡店里面有两种咖啡:白咖啡和黑咖啡。Java代码如下:
public abstract class Coffee {
private String description; //咖啡简介
public Coffee(String description) {
this.description = description;
}
public String getDescription() {
return description;
}
public abstract double cost();
public static void main(String[] args) {
Coffee c1 = new BlackCoffee("black");
Coffee c2 = new WhiteCoffee("white");
System.out.println("c1: " + c1.getDescription() + " cost: " + c1.cost());
System.out.println("c2: " + c2.getDescription() + " cost: " + c2.cost());
}
}
class BlackCoffee extends Coffee{
public BlackCoffee(String description) {
super(description);
}
@Override
public double cost() {
return 5.0;
}
}
class WhiteCoffee extends Coffee{
public WhiteCoffee(String description) {
super(description);
}
@Override
public double cost() {
return 6.0;
}
}
随着咖啡店的规模越来越大,咖啡店顾客的要求越来越高,有些顾客要求加牛奶,有些要求加糖。这是要如何修改Java代码呢?
public abstract Coffee{}
class WhiteCoffee extends Coffee{}
class BlackCoffee extends Coffee{}
class MilkWhiteCoffee extends WhiteCoffee{}
class SugarWhiteCoffee extends WhiteCoffee{}
class MilkBlackCoffee extends WhiteCoffee{}
class SugarBlackCoffee extends WhiteCoffee{}
上面这种方法看似简单直接,但如果又有一些顾客要求加牛奶和糖呢?某一天咖啡店要推出新品灰咖啡呢?那么我们需要写的代码量将会非常的大。
另外一种解决方案:
package designpattern.decorator;
public abstract class Coffee2 {
private String description;
private boolean sugar;
private boolean milk;
public Coffee2(String description) {
this.description = description;
}
public boolean isSugar() {
return sugar;
}
public void setSugar(boolean sugar) {
this.sugar = sugar;
}
public boolean isMilk() {
return milk;
}
public void setMilk(boolean milk) {
this.milk = milk;
}
public String getDescription() {
return description;
}
public abstract double cost();
}
class WhiteCoffee2 extends Coffee2 {
public WhiteCoffee2(String description) {
super(description);
}
@Override
public double cost() {
return 5.0;
}
}
class BlackCoffee2 extends Coffee2 {
public BlackCoffee2(String description) {
super(description);
}
@Override
public double cost() {
return 6.0;
}
}
这样子,我们如果有顾客需要新配料,那么我们只需要在Coffee2
增加新变量和setter/getter即可。推出新品,则创建要给新的类来继承Coffee2。这种方法比上一种方便很多,但也存在一些弊端:
- 如果有新配料,那么我们必须修改
Coffee2
; - 如果有顾客需要两份糖,如何表示?
- 其它。。。。
所以,真正解决办法的设计模式是:装饰者模式:
左边的是Coffee的实体类,右边是装饰类,即配料。每一个装饰类中都有一个实体类实例变量,表示,这个类型的实体(咖啡)被这个装饰类装饰(添加配料)。为什么在需要CondimentDecorator?并且getDescription()为abstract?我们慢慢往下看。
首先,我们来实现一下这个装饰者模式:
package designpattern.decorator;
public abstract class Coffee {
String description = "Unknow Type"; //咖啡简介
public String getDescription() {
return description;
}
public abstract double cost();
public static void main(String[] args) {
Coffee c1 = new WhiteCoffee("white coffee");//白咖啡
c1 = new Milk(c1);//白咖啡加奶
c1 = new Sugar(c1);//白咖啡加糖加奶
System.out.println(c1.getDescription() + " ¥" + c1.cost());
}
}
class BlackCoffee extends Coffee{
public BlackCoffee(String description) {
this.description = description;
}
@Override
public double cost() {
return 5.0;
}
}
class WhiteCoffee extends Coffee{
public WhiteCoffee(String description) {
this.description = description;
}
@Override
public double cost() {
return 6.0;
}
}
abstract class CondimentDecorator extends Coffee {
Coffee coffee;
public abstract String getDescription();
}
class Milk extends CondimentDecorator {
public Milk(Coffee coffee) {
this.coffee = coffee;
}
@Override
public double cost() {
return coffee.cost() + 1.0;
}
@Override
public String getDescription() {
return coffee.getDescription() + " " + ", milk ";
}
}
class Sugar extends CondimentDecorator {
public Sugar(Coffee coffee) {
this.coffee = coffee;
}
@Override
public double cost() {
return coffee.cost() + 0.5;
}
@Override
public String getDescription() {
return coffee.getDescription() + ", sugar";
}
}
2. 使用装饰者模式,自己实现一个LowerCaseIO
import java.io.*;
/**
* FilterInputStream是abstract decorator
* LowerCaseIO是concrete decorator
*/
public class LowerCaseIO extends FilterInputStream {
/**
* 调用父类构造方法。InputStream作为符类的成员变量,我们需要调用父类构造函数来初始化它。
* Creates a <code>FilterInputStream</code>
* by assigning the argument <code>in</code>
* to the field <code>this.in</code> so as
* to remember it for later use.
*
* @param in the underlying input stream, or <code>null</code> if
* this instance is to be created without an underlying stream.
*/
protected LowerCaseIO(InputStream in) {
super(in);
}
public int read() throws IOException {
int c = super.read();
return c == -1 ? c : Character.toLowerCase((char)c);
}
public int read(byte[] bytes, int off, int len) throws IOException {
int result = super.read(bytes, off, len);
for(int i = off; i < len + off; i++) {
bytes[i] = (byte) Character.toLowerCase((char)bytes[i]);
}
return result;
}
public static void main(String[] args) throws IOException {
InputStream stream = new LowerCaseIO(new BufferedInputStream(new FileInputStream("D:\\test.txt")));
byte[] bytes = new byte[10];
int i = stream.read(bytes, 0, 10);
System.out.println("读取了" + i + "个字节");
for(byte b: bytes) {
System.out.print((char)b);
}
}
}