(Too many)
(Flyweight: too many objects)
享元的特点,在另外的一些设计模式中,用来提高性能。通常的想法是为系统中的每一条项目都简单的创建一个对象,但是这样会产生大量的对象,这样会导致运行缓慢或者内存溢出。
享元通过减少对象的数量来解决这个问题。为了这样做,需要外部化一些对象内部的数据,因此看起来对象要比实际的多。然而,这样增加了使用这样对象的接口复杂性。因为必须传递足够的信息到方法调用中来告诉方法如何找到那些外部化的信息。
作为一个非常简单的例子,考虑一个数据点对象拥有整型,浮点型,和一个id记录对象的数目。假设你创建100万个这样的对象,向如下的例子来控制:
//: flyweight:ManyObjects.java
class DataPoint {
private static int count = 0;
private int id = count++;
private int i;
private float f;
public int getI() { return i; }
public void setI(int i) { this.i = i; }
public float getF() { return f; }
public void setF(float f) { this.f = f; }
public String toString() {
return "id: " + id + ", i = " + i + ", f = " + f;
}
}
public class ManyObjects {
static final int size = 1000000;
public static void main(String[] args) {
DataPoint[] array = new DataPoint[size];
for(int i = 0; i < array.length; i++)
array[i] = new DataPoint();
for(int i = 0; i < array.length; i++) {
DataPoint dp = array[i];
dp.setI(dp.getI() + 1);
dp.setF(47.0f);
}
System.out.println(array[size -1]);
}
} ///:~
依赖于你的计算机,这个程序也许会运行几秒钟。更多复杂的对象和更多涉及的操作也许会运行不下去。解决这个问题,DataPoint对象能够从100万个对象减少到一个对象,通过外部化DataPoint类内部的数据:
//: flyweight:FlyWeightObjects.java
class ExternalizedData {
static final int size = 5000000;
static int[] id = new int[size];
static int[] i = new int[size];
static float[] f = new float[size];
static {
for(int i = 0; i < size; i++)
id[i] = i;
}
}
class FlyPoint {
private FlyPoint() {}
public static int getI(int obnum) {
return ExternalizedData.i[obnum];
}
public static void setI(int obnum, int i) {
ExternalizedData.i[obnum] = i;
}
public static float getF(int obnum) {
return ExternalizedData.f[obnum];
}
public static void setF(int obnum, float f) {
ExternalizedData.f[obnum] = f;
}
public static String str(int obnum) {
return "id: " +
ExternalizedData.id[obnum] +
", i = " +
ExternalizedData.i[obnum] +
", f = " +
ExternalizedData.f[obnum];
}
}
public class FlyWeightObjects {
public static void main(String[] args) {
for(int i = 0; i < ExternalizedData.size; i++) {
FlyPoint.setI(i, FlyPoint.getI(i) + 1);
FlyPoint.setF(i, 47.0f);
}
System.out.println(
FlyPoint.str(ExternalizedData.size -1));
}
} ///:~
因此所有的数据都外部化到ExternalizedData类中,FlyPoint方法的每个调用都必须包含ExternalizedData类的索引。一致性起见,并且提醒读者注意方法调用中隐含的this指针,“this index”是作为第一个参数被传递进去的。
自然的,值得一提的是防范提早优化。“首先要启动,然后才变快—如果必须的话”。并且,不能靠猜,而是要靠一个模具工具来检测性能瓶颈。
(Decorator: too many classes)
分层的对象应用到动态的和透明的添加职责到被引用的个体对象为修饰模式。
当子类化时创建过多类的时候使用。
所有的包装最初对象的修饰者必须有同样的基类接口。
动态的 Proxy/surrogate?
应用于特异的继承结构
衡量:使用修饰模式会让代码变得复杂。
(Basic decorator structure)
考虑去当地的一个咖啡馆,BeanMeUp,来上一杯。那里有许多不同种类的饮料供应—浓咖啡,lattes,茶,冰咖啡,热巧克力等等,也有很多额外的(当然需要额外加钱)例如生奶油或格外的浓咖啡粉。你也可以作出某种改变而不必而外付账,比如来一杯无咖啡因咖啡而非一般的咖啡。
相当清楚的如果我们模型化所有的这些饮料和组合,会有一个相当大的类图。为了清楚的描述我们仅仅选择这些咖啡的一个子集:Espresso, Espresso Con panna, Café late, Cappuccino ,Café Mocha. 还包括两个添加品:whipped cream(“whipped”) ,额外的espresso粉;还有三种变化:decaf, steamed milk(“wet”), foamed milk(“dry”)。
(Class for each combination)
一个方案是每种组合创建一个类。每个类描述饮料和价格等。结果菜单是巨大的,部分的类图看起来如下:
这是其中的一个组合,一个简单的Cappuccino的实现:
class Cappuccino {
private float cost = 1;
private String description = "Cappucino";
public float getCost() {
return cost;
}
public String getDescription() {
return description;
}
}
使用这种方法的的关键在于找到你想要的特定组合。因此,一旦你找到你喜欢的饮料,下面的CoffeeShop类就是你将怎样使用它:
//: decorator:nodecorators:CoffeeShop.java
// Coffee example with no decorators
package decorator.nodecorators;
import junit.framework.*;
class Espresso {}
class DoubleEspresso {}
class EspressoConPanna {}
class Cappuccino {
private float cost = 1;
private String description = "Cappucino";
public float getCost() {
return cost;
}
public String getDescription() {
return description;
}
}
class CappuccinoDecaf {}
class CappuccinoDecafWhipped {}
class CappuccinoDry {}
class CappuccinoDryWhipped {}
class CappuccinoExtraEspresso {}
class CappuccinoExtraEspressoWhipped {}
class CappuccinoWhipped {}
class CafeMocha {}
class CafeMochaDecaf {}
class CafeMochaDecafWhipped {
private float cost = 1.25f;
private String description =
"Cafe Mocha decaf whipped cream";
public float getCost() {
return cost;
}
public String getDescription() {
return description;
}
}
class CafeMochaExtraEspresso {}
class CafeMochaExtraEspressoWhipped {}
class CafeMochaWet {}
class CafeMochaWetWhipped {}
class CafeMochaWhipped {}
class CafeLatte {}
class CafeLatteDecaf {}
class CafeLatteDecafWhipped {}
class CafeLatteExtraEspresso {}
class CafeLatteExtraEspressoWhipped {}
class CafeLatteWet {}
class CafeLatteWetWhipped {}
class CafeLatteWhipped {}
public class CoffeeShop extends TestCase {
public void testCappuccino() {
// This just makes sure it will complete
// without throwing an exception.
// Create a plain cappuccino
Cappuccino cappuccino = new Cappuccino();
System.out.println(cappuccino.getDescription()
+ ": $" + cappuccino.getCost());
}
public void testCafeMocha() {
// This just makes sure it will complete
// without throwing an exception.
// Create a decaf cafe mocha with whipped
// cream
CafeMochaDecafWhipped cafeMocha =
new CafeMochaDecafWhipped();
System.out.println(cafeMocha.getDescription()
+ ": $" + cafeMocha.getCost());
}
public static void main(String[] args) {
junit.textui.TestRunner.run(CoffeeShop.class);
}
} ///:~
下面是相应的输出:
Cappucino: $1.0
Cafe Mocha decaf whipped cream: $1.25
可以看到,创建特定的组合是容易的,因为你仅仅创建一个类的一个实例。然而,这种方式有大量的问题。 这些组合是静态的固定的,因此任何组合在客户需要之前被创建。第二,结果菜单太大了,找到特定组合是困难的和耗时的。
(The decorator approach)
另一个方式将打破饮料到各种组件,例如esppresso 和foamed milk,然后,让客户组合组件描述特定的咖啡。
为了完成这个,我们使用修饰模式。一个修饰器增加职责到一个组件通过封装它,但是修饰器依从于它所封装组件的接口,因此封装是透明的。修饰器也能够在没有损失透明性下嵌套使用。
修饰器中被调用的方法能够依次调用组件中的方法,当然能够在调用前后执行。
因此当我们添加getTotalCost() 和 getDescription()方法到DrinkComponent接口,Esppresso看起来如下:
class Espresso extends Decorator {
private float cost = 0.75f;
private String description = " espresso";
public Espresso(DrinkComponent component) {
super(component);
}
public float getTotalCost() {
return component.getTotalCost() + cost;
}
public String getDescription() {
return component.getDescription() +
description;
}
}
如下面代码所示,你组合组件去创建下面的饮料:
//: decorator:alldecorators:CoffeeShop2.java
// Coffee example using decorators
package decorator.alldecorators;
import junit.framework.*;
interface DrinkComponent {
String getDescription();
float getTotalCost();
}
class Mug implements DrinkComponent {
public String getDescription() {
return "mug";
}
public float getTotalCost() {
return 0;
}
}
abstract class Decorator implements DrinkComponent
{
protected DrinkComponent component;
Decorator(DrinkComponent component) {
this.component = component;
}
public float getTotalCost() {
return component.getTotalCost();
}
public abstract String getDescription();
}
class Espresso extends Decorator {
private float cost = 0.75f;
private String description = " espresso";
public Espresso(DrinkComponent component) {
super(component);
}
public float getTotalCost() {
return component.getTotalCost() + cost;
}
public String getDescription() {
return component.getDescription() +
description;
}
}
class Decaf extends Decorator {
private String description = " decaf";
public Decaf(DrinkComponent component) {
super(component);
}
public String getDescription() {
return component.getDescription() +
description;
}
}
class FoamedMilk extends Decorator {
private float cost = 0.25f;
private String description = " foamed milk";
public FoamedMilk(DrinkComponent component) {
super(component);
}
public float getTotalCost() {
return component.getTotalCost() + cost;
}
public String getDescription() {
return component.getDescription() +
description;
}
}
class SteamedMilk extends Decorator {
private float cost = 0.25f;
private String description = " steamed milk";
public SteamedMilk(DrinkComponent component) {
super(component);
}
public float getTotalCost() {
return component.getTotalCost() + cost;
}
public String getDescription() {
return component.getDescription() +
description;
}
}
class Whipped extends Decorator {
private float cost = 0.25f;
private String description = " whipped cream";
public Whipped(DrinkComponent component) {
super(component);
}
public float getTotalCost() {
return component.getTotalCost() + cost;
}
public String getDescription() {
return component.getDescription() +
description;
}
}
class Chocolate extends Decorator {
private float
来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/25966/viewspace-53319/,如需转载,请注明出处,否则将追究法律责任。