装饰者模式
这简直就是“类爆炸”。class explosion。
利用继承设计子类的行为,是在编译时静态决定的,而且所有的子类都会继承到相同的行为。然而,如果能够利用组合的做法扩展对象的行为,就可以在运行时动态地进行扩展。
笨透了,干嘛设计这么多类,利用实例变量和继承,就可以追踪这些变量啊!;好吧!就来试试。先从Beverage类下手,加上实例变量代表是否加上调料(牛奶,豆浆等)。
定义了父类后,加入子类,超类cost()将计算所有调料的价钱,而子类覆盖过的cost()会扩展超类的功能,把指定的饮料类型的价钱也加进来。
伪代码如下: public class Beverage { //为milkCost,soyCost等增加实例变量 //为milk soy 声明getter和setter方法 public double cost() { float condimentCost=0.0; if(hasMilk()){ condimentCost+=milkCost; } if(hasSoy()){ condimetnCost+=soyCost; } ..... return condimetnCost; } public class DarkRoast extends Beverage{ public DarkRoast(){ description="most dark roast"; } public double cost(){ return 1.99+super.cost(); } }
认识装饰者模式
定义装饰者模式:
装饰我们的对象
装饰者可以增加一些新方法,新行为是通过在旧行为前面或后面做一些计算来添加的。
好吧,让starbuck也能符合我们的框架:
代码如下:
public abstract class Beverage { String description="unknown beverage"; public String getDescription() { return description; } public abstract double cost(); }
public abstract class CondimentDecorator extends Beverage { public abstract String getDescription(); }
public class HouseBlend extends Beverage { public HouseBlend() { description = "House Blend Coffee"; } public double cost() { return .89; } }
public class HouseBlend extends Beverage { public HouseBlend() { description = "House Blend Coffee"; } public double cost() { return .89; } }
可以自行添加其他的饮料类,做法一样。
写调料代码。
我们已经完成了抽象组件(Beverage),有了具体组件(HouseBlend),也有了抽象装饰者(condimentDecroator),现在,我们来实现具体装饰者。
public class Mocha extends CondimentDecorator { Beverage beverage; public Mocha(Beverage beverage) { this.beverage = beverage; } public String getDescription() { return beverage.getDescription() + ", Mocha"; } public double cost() { return .20 + beverage.cost(); } }
订单的测试代码如下:
public class StarbuzzCoffee { public static void main(String args[]) { 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()); } }
% java StarbuzzCoffee
Espresso $1.99
Dark Roast Coffee, Mocha, Mocha, Whip $1.49
House Blend Coffee, Soy, Mocha, Whip $1.34
问:装饰者知道这一连串装饰链条中其他装饰者的存在吗?比方说,我想要让getDescription输出whip,Double Mocha,而不是
Mochar Whip Mochar ,这需要最外圈的装饰者知道有哪些装饰者牵涉其中了。
装饰者该做的是,就是添加行为到被包装的对象上,当需要窥视装饰者链中的每一个装饰者时,就超出他们的天赋了。但是,并不是做不到,可以写一个CondimentPrettyPrint装饰者,然后把whip,Double Mocha 变成
Mochar Whip Mochar ,如果能把getDescription的返回值变成ArrayList类型,让每个调料名称独立开来,那么这个函数更加容易写。
真实世界的装饰者:java i/o:
java包内的类太多了,其中许多类都是装饰者。下面是一个典型的对象几乎。用装饰者将功能结合起来,以读取文件数据。
BufferedInputStream及LineNumberInputStream都扩展自FilterInputStream,而FilterInputStream是一个抽象的装饰类。
我们可以组合各种”输入“流装饰者来符合你的用途。
你会发现”输出“流的设计也是一样。可能还会发现Reader/witer也是一样。
但是java i/o也引出装饰者模式的一个缺点:利用装饰者模式,常常造成设计中有大量的小类,数量实在太多,可能会造成困扰。
package headfirst.decorator.io; import java.io.*; public class LowerCaseInputStream extends FilterInputStream { public LowerCaseInputStream(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[] b, int offset, int len) throws IOException { int result = super.read(b, offset, len); for (int i = offset; i < offset+result; i++) { b[i] = (byte)Character.toLowerCase((char)b[i]); } return result; } }
package headfirst.decorator.io; import java.io.*; public class InputTest { public static void main(String[] args) throws IOException { int c; try { InputStream in = new LowerCaseInputStream( new BufferedInputStream( new FileInputStream("test.txt"))); while((c = in.read()) >= 0) { System.out.print((char)c); } in.close(); } catch (IOException e) { e.printStackTrace(); } } }
要点:
自己写的装饰者demo:
先构造一个基础类Horse,在构造一个装饰者HorseChild。重写run方法。
public class Horse { protected int height; protected int weight; public Horse(int height,int weight) { this.height=height; this.weight=weight; } public Horse(Horse horse) { height=horse.height; weight=horse.weight; } public void run() { System.out.println("run fast"); }
HorseChild如下:
public class HorseChild extends Horse{ public HorseChild() { super(50,30); } public HorseChild(Horse horse) { super(horse); } public void run() { super.run(); System.out.println("run fast more"); } }
在
public HorseChild(Horse horse) 遇到了很大的问题。最开始这么写:
public HorseChild(Horse horse)
{
weight=horse.weight;
height=horse.weight;
}
报错:Implicit super constructor Horse() is undefined. Must explicitly invoke another constructor
跟c++一样,构造子类时都会先调用父类的构造函数,这里我们没有用super去调用父类中我们定义的构造函数,那么就应该去调用默认的构造函数。
但是java跟c++一样,一旦我们定义了自己的构造函数后,就不在产生默认的构造函数Horse(),所以我们必须显式定义不带参数的构造函数:
public Horse()
{
}
加上这个之后,就运行正常了。
以下来自csdn帖子:
class Aa { public Aa(String a,String b) { } } public class Bb extends Aa { public Bb(String a,String b) {//隐掉下面那句话后在此构造函数处提示:Implicit super constructor Aa() is undefined. Must explicitly invoke another constructor,为什么会这样啊? // super(a,b); } public static void main(String args []) { Aa a = new Aa("Hi","Tom"); Aa b = new Bb("Hi","Bart"); } }
答:
子类构造器:
public Bb(String a,String b)
{ }
若没有明确调用super()或this(),则编译程序会自动帮它生成一个,即:
public Bb(String a,String b)
{ }
等价于:
public Bb(String a,String b)
{
super();//这是编译程序自动帮它生成
}
而在父类Aa中,又没有定义默认构造器,故报错.
注:编译程序之所以会自动帮它生成super();,主要是确保:子类对象中所包含的父对象空间一定会先初始化.
子类的构造方法上如果没有明确的调用父类的构造方法,它会默认地调用父类的不带参数的方法
如果父类有定义带了参数的构造方法但是没有定义不带参数的构造方法,则系统不会默认为其创建个不带参数的构造方法
这样子类调用的时候必然就出错咯。http://topic.csdn.net/u/20090224/17/7745d504-3d9e-46a1-98a8-7c21760cf5ea.html