如何理解享元模式?“享”即共享,“元”指的是元件,也就是小颗粒的东西,“享元”顾名思义便是共享小部件。很多的系统或者程序包含大量的对象,但是这些对象绝大多数都是差不多的,除了一些极个别的属性外。那么也就是说,在一个系统或者程序中有多个相同的对象的时候,我们只需要共享一份就可以了,不必去实例化每一个对象。
享元模式: 运用共享技术有效地支持大量细粒度的对象。
1. Flyweight: 是所有具体享元类的超类或者接口,通过这个接口Flyweight可以接受并且作用于外部状态。
2. ConcreteFlyweight: 继承Flyweight超类或者实现Flyweight接口的类,并为内部状态增加存储空间。
3. UnsharedConcreteFlyweight: 并不需要共享的Flyweight的子类。Flyweight接口使得共享成为了可能,但是它并不强制共享。
4. FlyweightFactory: 创建并且管理Flyweight对象。确保合理的共享Flyweight。当用户请求一个Flyweight对象时,FlyweightFactory会去做检查,如果找到请求的Flyweight对象则返回该对象,否则将创建一个新的对象并存储供后续使用。
内部状态: 可以共享,状态不会随着环境改变而改变
外部状态: 不可以共享,状态会随着环境改变而改变
我就拿买衣服举个例子。比方说去商城买衬衫(Shirt)、卫衣(Sweater)、休闲裤(Slacks)这些服饰(我们此处仅考虑服饰的颜色和大小)。不管服饰的颜色如何、大小如何,最后所买下服饰的类型只有三种。
1. 创建一个享元接口 Clothes
public interface Clothes {
void buy(UnsharedConcreteClothes unsharedConcreteClothes);
}
2. 创建具体的享元类 ConcreteClothes
public class ConcreteClothes implements Clothes {
private String name;
private String size;
private String color;
public ConcreteClothes(String name) {
this.name = name;
}
public void setSize(String size) {
this.size = size;
}
public void setColor(String color) {
this.color = color;
}
@Override
public void buy(UnsharedConcreteClothes unsharedConcreteClothes) {
System.out.println("ConcreteClothes [name=" + name + ", size=" + size + ", color=" + color + ", price="
+ unsharedConcreteClothes.getPrice() + "]");
}
}
3. 创建非共享享元实现类 UnsharedConcreteClothes
public class UnsharedConcreteClothes implements Clothes {
private String price;
public UnsharedConcreteClothes(String price) {
this.price = price;
}
public String getPrice() {
return price;
}
public void setPrice(String price) {
this.price = price;
}
@Override
public void buy(UnsharedConcreteClothes unsharedConcreteClothes) {
System.out.println("UnsharedConcreteClothes [price=" + price + "]");
}
}
4. 创建享元工厂类 ClothesFactory
public class ClothesFactory {
private static final HashMap clothesMap = new HashMap<>();
public static Clothes getClothes(String name) {
ConcreteClothes clothes = (ConcreteClothes)clothesMap.get(name);
if (clothes == null) {
System.out.println("Instance of name = " + name + " does not exists, creating it!");
clothes = new ConcreteClothes(name);
System.out.println("Instance of name = " + name + " created!");
clothesMap.put(name, clothes);
}
return clothesMap.get(name);
}
public static void getClothesTypeCount() {
System.out.println("Type of clothes is " + clothesMap.size() + " !");
}
}
5. 创建享元测试类 FlyweightTest
public class FlyweightTest {
private static final String[] name = { "Shirt", "Sweater", "Slacks" };
private static final String[] size = { "S", "M", "L", "XL", "XXL" };
private static final String[] color = { "Khaki", "ArmyGreen", "White", "Black" };
private static final String[] price = { "¥99", "¥129", "¥149", "¥199" };
public static String getRandom(String[] strs) {
return strs[new Random().nextInt(strs.length)];
}
public static void main(String[] args) {
for (int i = 0; i < 8; i++) {
ConcreteClothes concreteClothes = (ConcreteClothes) ClothesFactory.getClothes(getRandom(name));
concreteClothes.setSize(getRandom(size));
concreteClothes.setColor(getRandom(color));
UnsharedConcreteClothes unsharedConcreteClothes = new UnsharedConcreteClothes(getRandom(price));
concreteClothes.buy(unsharedConcreteClothes);
}
ClothesFactory.getClothesTypeCount();
}
}
运行结果如下:
Instance of name = Slacks does not exists, creating it!
Instance of name = Slacks created!
ConcreteClothes [name=Slacks, size=L, color=Black, price=¥99]
Instance of name = Shirt does not exists, creating it!
Instance of name = Shirt created!
ConcreteClothes [name=Shirt, size=L, color=Black, price=¥99]
ConcreteClothes [name=Slacks, size=S, color=ArmyGreen, price=¥199]
Instance of name = Sweater does not exists, creating it!
Instance of name = Sweater created!
ConcreteClothes [name=Sweater, size=L, color=ArmyGreen, price=¥129]
ConcreteClothes [name=Shirt, size=M, color=White, price=¥99]
ConcreteClothes [name=Shirt, size=XL, color=White, price=¥199]
ConcreteClothes [name=Sweater, size=L, color=White, price=¥99]
ConcreteClothes [name=Shirt, size=L, color=Khaki, price=¥199]
Type of clothes is 3 !
本例中,name、size、color这些个是可以共享的,可以认为是内部状态。而price可能会受到季节、打折活动等外部环境变化的影响,可以认定为是外部状态。
在Java语言当中,String类型就使用了享元模式。我们知道String对象是final修饰的,也就是说对象一旦创建就不能改变。在Java中字符串常量都是存在常量池中的,Java会去确保一个字符串常量在常量池中仅有一个拷贝。