附链
你也可以在这些平台阅读本文:
定义
运用共享技术有效地支持大量细粒度的对象。
“享”即共享,“元”指的是元件,也就是小颗粒的东西。“享元”顾名思义就是共享小部件。
很多的系统程序包含大量的对象,但是这些对象绝大多数都是差不多的,除了一些极个别的属性外。
那么也就是说,在一个系统程序中有多个相同对象的时候,我们只需要共享一份就可以了,不必去实例化每一个对象。
内部状态与外部状态
享元模式是区分内部状态与外部状态的:
- 内部状态:可以共享,在享元对象的内部,状态不会随着环境改变而改变
- 外部状态:不可以共享,在享元对象的外部,状态会随着环境改变而改变
场景示例
笔者这里以衣服为例。将衣服的品牌作为内部属性共享,将衣服的大小、成分、价格作为外部属性使用。
创建衣服类
/**
* @author zhh
* @description 衣服类
* @date 2020-02-16 22:36
*/
public interface Clothes {
/**
* 衣服信息
*/
void info();
}
创建衬衣类
/**
* @author zhh
* @description 衬衣类
* @date 2020-02-16 22:37
*/
public class Shirt implements Clothes {
/**
* 品牌
*/
private String brand;
/**
* 大小
*/
private String size;
/**
* 成分
*/
private String component;
/**
* 价格
*/
private String price;
public Shirt(String brand) {
this.brand = brand;
}
public void setSize(String size) {
this.size = size;
}
public void setComponent(String component) {
this.component = component;
}
public void setPrice(String price) {
this.price = price;
}
public void info() {
System.out.println(String.format("当前衬衣信息: {品牌: %s, 大小: %s, 成分: %s, 价格: %s}", brand, size, component, price));
}
}
在该场景中,笔者将品牌属性作为其内部状态,在对象创建的初期就初始化完成,状态不会随着外部环境变化,可以共享。
而将大小、成分、价格这些属性当做外部状态,通过 setter
方法供外部修改,状态会随外部环境变化,这些属性不能够共享。
创建衬衣工厂类
享元模式中通常会出现工厂模式,需要创建一个享元工厂来负责维护一个享元池。
/**
* @author zhh
* @description 衣服工厂类
* @date 2020-02-16 22:48
*/
public class ClothesFactory {
private static final Map<String, Clothes> CLOTHES_MAP = new HashMap<String, Clothes>();
/**
* 获取衬衣
* @param brand 品牌
* @return
*/
public static Clothes getShirt(String brand) {
Shirt shirt = (Shirt) CLOTHES_MAP.get(brand);
if (shirt == null) {
shirt = new Shirt(brand);
CLOTHES_MAP.put(brand, shirt);
System.out.println("创建新的品牌衬衣, 品牌为: " + brand);
}
return shirt;
}
}
测试类及输出
方便起见,在该测试类中随机模拟数据。
/**
* @author zhh
* @description 测试类
* @date 2020-02-16 23:04
*/
public class Test {
private static final String[] BRAND = {"YOUNGOR雅戈尔", "FIRS杉杉", "ROMON罗蒙", "Hodo红豆"};
private static final String[] SIZE = {"S", "M", "L", "XL", "XXL"};
private static final String[] COMPONENT = {"棉", "涤纶", "真丝"};
private static final String[] PRICE = { "¥99", "¥129", "¥149", "¥199" };
/**
* 随机模拟
* @param strs 随机模拟数组
* @return
*/
public static String getRandom(String[] strs) {
return strs[new Random().nextInt(strs.length)];
}
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
Shirt shirt = (Shirt) ClothesFactory.getShirt(getRandom(BRAND));
shirt.setSize(getRandom(SIZE));
shirt.setComponent(getRandom(COMPONENT));
shirt.setPrice(getRandom(PRICE));
shirt.info();
}
}
}
测试类的输出结果如下:
创建新的品牌衬衣, 品牌为: ROMON罗蒙
当前衬衣信息: {品牌: ROMON罗蒙, 大小: M, 成分: 真丝, 价格: ¥129}
创建新的品牌衬衣, 品牌为: FIRS杉杉
当前衬衣信息: {品牌: FIRS杉杉, 大小: S, 成分: 棉, 价格: ¥199}
当前衬衣信息: {品牌: ROMON罗蒙, 大小: M, 成分: 棉, 价格: ¥99}
创建新的品牌衬衣, 品牌为: Hodo红豆
当前衬衣信息: {品牌: Hodo红豆, 大小: M, 成分: 棉, 价格: ¥99}
当前衬衣信息: {品牌: FIRS杉杉, 大小: XL, 成分: 真丝, 价格: ¥99}
当前衬衣信息: {品牌: ROMON罗蒙, 大小: XXL, 成分: 真丝, 价格: ¥99}
当前衬衣信息: {品牌: Hodo红豆, 大小: M, 成分: 真丝, 价格: ¥99}
当前衬衣信息: {品牌: Hodo红豆, 大小: M, 成分: 涤纶, 价格: ¥149}
创建新的品牌衬衣, 品牌为: YOUNGOR雅戈尔
当前衬衣信息: {品牌: YOUNGOR雅戈尔, 大小: XXL, 成分: 真丝, 价格: ¥129}
当前衬衣信息: {品牌: ROMON罗蒙, 大小: XL, 成分: 涤纶, 价格: ¥199}
类结构图
以上示例类的结构图如下所示
总结
适用场景
- 应用于系统底层开发,解决系统性能问题。
- 系统有大量相似对象,需要缓冲池。
优点
- 共享相同或者相似的细粒度对象,减少对象的创建,降低系统内存,提高效率。
- 外部状态相对独立,对象可以在不同的环境中被复用。
缺点
- 读取外部状态使得运行时间变长。
- 内部状态与外部状态分离,使得程序的逻辑复杂化。