什么是享元模式?
享元模式是一种对象结构型模式。
享元模式是将一系列细粒度的对象放入到容器中并对外共享。
在JVM中,每当我们实现一个字符串或者整形时,内存中都会一块空间与之对应,但是每次生产、销毁的代价是昂贵的,JVM通过实践不断地进行优化,最终出现了运行时常量池、IntegerCache等产物,这其实就是运用了享元模式。
常用的字符串(比如说java)和在默认情况下的-127~128都是最细粒度的对象,运行时常量池和缓存就是容器。
而在我们实际开发中,ThreadPool也是享元模式的具体实现。
核心结构
- Flyweight。抽象享元,声明外部状态和内部状态的方法。
- ConcreteFlyweight。具体享元,实现抽象享元声明的外部状态和内部状态的方法,每个具体享元提供了一个唯一的享元对象。
- UnsharedConcreteFlyweight。非共享具体享元,享元对象可以共享也可以不共享,如果需要不共享的享元对象,可以使用非共享具体享元,当需要一个非共享享元对象时,可以直接实例化创建。
- FlyweightFactory。享元工厂,用于创建并维护享元对象,它针对抽象享元进行编程,同时将具体享元维护在一个容器中,在实际中一般结合工厂模式进行享元工厂设计。
类图展示
代码分析
代码展示部分,笔者偷懒举了一个简单享元模式,笔者将会详细分析Java线程池作为弥补。
六月苹果的WWDC开发者大会,苹果出乎意料的对硬件进行了升级,对于MacBook,其实13寸和15寸的模子几乎是一样的(TouchBar版),所以我们根据此例来实现享元模式。
首先声明抽象享元——MacBook类:
/**
* Created by <sunshine> mysunshinedreams@163.com on 2017/1/20 0020.
* 享元模式——抽象享元
*/
public abstract class Macbook {
protected String size;
public Macbook(String size) {
this.size = size;
}
public void produceMacbook(String color) {
System.out.println("produce macbook which size is ".concat(this.size).concat(" and size is ".concat(color)));
}
}
继续,我们创造两个具体享元。
13寸的MacBook:
/**
* Created by <sunshine> mysunshinedreams@163.com on 2017/1/20 0020.
* 享元模式——具体享元
*/
public class ThirteenSizeMacbook extends Macbook {
public ThirteenSizeMacbook(String size) {
super(size);
}
}
15寸的MacBook:
/**
* Created by <sunshine> mysunshinedreams@163.com on 2017/1/20 0020.
* 享元模式——具体享元
*/
public class FifteenSizeMacbook extends Macbook {
public FifteenSizeMacbook(String size) {
super(size);
}
}
有了享元对象,我们还需要一个容器来装载它们:
/**
* Created by <sunshine> mysunshinedreams@163.com on 2017/1/20 0020.
* 享元模式——工厂
*/
public class MacbookFactory {
private static MacbookFactory macbook = new MacbookFactory();
private static Map<String, Macbook> macbookMap;
private MacbookFactory() {
macbookMap = new HashMap<String, Macbook>();
Macbook fifteenSizeMacbook = new FifteenSizeMacbook("15");
Macbook thirteenSizeMacbook = new ThirteenSizeMacbook("13");
macbookMap.put("13", thirteenSizeMacbook);
macbookMap.put("15", fifteenSizeMacbook);
}
public static MacbookFactory getInstance() {
return macbook;
}
public static Macbook getMacbook(String size) {
return macbookMap.get(size);
}
}
惯例,测试代码:
/**
* Created by <sunshine> mysunshinedreams@163.com on 2017/1/20 0020.
* 享元模式测试
*/
public class FlyWeightTest {
public static void main(String[] args) {
flyWeightTest();
}
private static void flyWeightTest() {
try {
MacbookFactory factory = MacbookFactory.getInstance();
// 获取两个13寸的macbook
Macbook thirteenSizeMacbookOne = MacbookFactory.getMacbook("13");
Macbook thirteenSizeMacbookTwo = MacbookFactory.getMacbook("13");
// 获取两个15寸的macbook
Macbook fifteenSizeMacbookOne = MacbookFactory.getMacbook("15");
Macbook fifteenSizeMacbookTwo = MacbookFactory.getMacbook("15");
// 两两比较对象
System.out.println(thirteenSizeMacbookOne == thirteenSizeMacbookTwo);
System.out.println(fifteenSizeMacbookOne == fifteenSizeMacbookTwo);
// 生产
thirteenSizeMacbookOne.produceMacbook("grey");
thirteenSizeMacbookTwo.produceMacbook("silver");
fifteenSizeMacbookOne.produceMacbook("grey");
fifteenSizeMacbookTwo.produceMacbook("silver");
} catch (Exception e) {
e.printStackTrace();
}
}
}
模式总结
优点
- 享元模式下,原子对象的外部状态相对独立,并且不会影响其内部状态,从而使得享元对象可以被共享。
- 极大的减少了内存中细粒度对象的数量,节省内存资源,提高了系统性能。
缺点
- 享元模式需要分离出原子对象的外部状态和内部状态,提高了系统设计的复杂度。
- 面向窄化,享元池或者享元对象设计不当,很可能造成系统资源的极大浪费。
适用场景
- 系统中含有大量相同或相似的细粒度对象。
- 对象的状态可以对外展示。
- 当创建或者销毁某些相同或相似对象的代价较高时,可以通过预先创建一个享元池,比如说,线程池。