👉文章示例代码👈
定义
运用共享技术有效地支持大量细粒度的对象。
“享”即共享,“元”指的是元件,也就是小颗粒的东西。“享元”顾名思义就是共享小部件。
很多的系统程序包含大量的对象,但是这些对象绝大多数都是差不多的,除了一些极个别的属性外。
那么也就是说,在一个系统程序中有多个相同对象的时候,我们只需要共享一份就可以了,不必去实例化每一个对象。
内部状态与外部状态
享元模式是区分内部状态与外部状态的:
- 内部状态:可以共享,在享元对象的内部,状态不会随着环境改变而改变
- 外部状态:不可以共享,在享元对象的外部,状态会随着环境改变而改变
场景示例
笔者这里以衣服为例。将衣服的品牌作为内部属性共享,将衣服的大小、成分、价格作为外部属性使用。
创建衣服类
1 2 3 4 5 6 7 8 9 10 11 12
|
public interface Clothes {
void info(); }
|
创建衬衣类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
|
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
方法供外部修改,状态会随外部环境变化,这些属性不能够共享。
创建衬衣工厂类
享元模式中通常会出现工厂模式,需要创建一个享元工厂来负责维护一个享元池。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
|
public class ClothesFactory {
private static final Map<String, Clothes> CLOTHES_MAP = new HashMap<String, Clothes>();
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; } }
|
测试类及输出
方便起见,在该测试类中随机模拟数据。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
|
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" };
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}
类结构图
以上示例类的结构图如下所示

总结
适用场景
- 应用于系统底层开发,解决系统性能问题。
- 系统有大量相似对象,需要缓冲池。
优点
- 共享相同或者相似的细粒度对象,减少对象的创建,降低系统内存,提高效率。
- 外部状态相对独立,对象可以在不同的环境中被复用。
缺点
- 读取外部状态使得运行时间变长。
- 内部状态与外部状态分离,使得程序的逻辑复杂化。
参考