享元模式(Flyweight),使用共享技术有效地支持大量细粒度的对象。常用于系统底层开发,解决系统的性能问题。比如,数据库连接池技术。
享元模式能够解决重复对象的内存浪费的问题,当系统中有大量相似对象,需要缓冲池时,不需总是创建新对象,可以从缓冲池里获取。这样可以降低系统内存消耗,同时提高效率。
享元模式经典的应用场景就是池技术了,String 常量池、数据库连接池、缓冲池等等都是享元模式的应用,它是池技术的重要实现方式。
要应用享元模式,我们需要将对象属性分为内部属性和外部属性。内部属性使对象唯一,而外部属性由客户端代码设置并用于执行不同的操作。
享元模式解析
角色介绍
Flyweight:它是所有具体享元类的超类或接口,通过这个接口,Flyweight 可以接受并作用于外部状态。
ConcreteFlyweight:它是继承 Flyweight 超类或实现 Flyweight 接口,并为内部状态增加存储空间。
UnsharedConcreteFlyweight:它是指那些不需要共享的Flyweight子类。因为 Flyweight 接口共享成为可能,但它并不强制共享。
FlyweightFactory:它是一个享元工厂,用来创建并管理 Flyweight 对象.它主要是用来确保合理地共享 Flyweight ,当用户请求一个 Flyweight 时,Flywei ghtFactory 对象提供一个 已创建的实例或者创建一个(如果不存在的话)。
享元模式基本代码
- Flyweight 类
1 | public interface Flyweight { |
2 | |
3 | void operation(int status); |
4 | } |
- ConcreteFlyweight 类
1 | public class ConcreteFlyweight implements Flyweight { |
2 | |
3 | |
4 | public void operation(int status) { |
5 | System.out.println("具体的 Flyweight: " + status); |
6 | } |
7 | } |
- UnshareConcreteFlyweight 类
1 | public class UnshareConcreteFlyweight implements Flyweight { |
2 | |
3 | |
4 | public void operation(int status) { |
5 | System.out.println("不共享的具体Flyweight: " + status); |
6 | } |
7 | } |
- FlyweightFactory 类
1 | public class FlyweightFactory { |
2 | |
3 | private static Map<String, Flyweight> flyweightMap = new HashMap<>(); |
4 | |
5 | private FlyweightFactory() { |
6 | |
7 | } |
8 | |
9 | public static Flyweight getFlyweight(String key) { |
10 | Flyweight flyweight = flyweightMap.get(key); |
11 | if (flyweight == null) { |
12 | if (Objects.equals(key, "A")) { |
13 | flyweight = new ConcreteFlyweight(); |
14 | } |
15 | |
16 | if (Objects.equals(key, "B")) { |
17 | flyweight = new ConcreteFlyweight(); |
18 | } |
19 | flyweightMap.put(key, flyweight); |
20 | } |
21 | |
22 | return flyweight; |
23 | } |
24 | |
25 | } |
- Client 类
1 | public class Client { |
2 | |
3 | public static void main(String[] args) { |
4 | // 外部状态 |
5 | int status = 2; |
6 | Flyweight flyweightA = FlyweightFactory.getFlyweight("A"); |
7 | flyweightA.operation(status); |
8 | |
9 | Flyweight flyweightB = FlyweightFactory.getFlyweight("B"); |
10 | flyweightB.operation(status + 1); |
11 | |
12 | |
13 | // 不共享的 Flyweight |
14 | UnshareConcreteFlyweight unshareConcreteFlyweight = new UnshareConcreteFlyweight(); |
15 | unshareConcreteFlyweight.operation(status + 2); |
16 | |
17 | } |
18 | } |
示例
假设我们需要使用线条和椭圆形创建图形。因此,我们将有一个接口 Shape 和它的具体实现为 Line 和 Oval。椭圆类将具有固有属性,以确定是否用给定的颜色填充椭圆,而 Line 将不具有任何固有属性。
使用享元模式实现
- Shape 类
1 | public interface Shape { |
2 | |
3 | public void draw(int x, int y, int width, int height, Color color); |
4 | } |
- Line 类
1 | public class Line implements Shape { |
2 | |
3 | public Line(){ |
4 | System.out.println("Creating Line object"); |
5 | } |
6 | |
7 | |
8 | public void draw(int x1, int y1, int x2, int y2, |
9 | Color color) { |
10 | System.out.println("Draw Line"); |
11 | } |
12 | |
13 | } |
- Oval 类
1 | public class Oval implements Shape { |
2 | |
3 | // 内部属性 |
4 | private boolean fill; |
5 | |
6 | public Oval(boolean isFill){ |
7 | this.fill = fisFill; |
8 | System.out.println("Creating Oval object"); |
9 | } |
10 | |
11 | public void draw(int x, int y, int width, int height, |
12 | Color color) { |
13 | System.out.println("Draw Oval"); |
14 | if(fill){ |
15 | System.out.println("Fill Oval with color"); |
16 | } |
17 | } |
18 | } |
- ShapeFactory 类
1 | public class ShapeFactory { |
2 | |
3 | private static final Map<ShapeType,Shape> shapes = new HashMap<ShapeType,Shape>(); |
4 | |
5 | public static Shape getShape(ShapeType type) { |
6 | Shape shape = shapes.get(type); |
7 | |
8 | if (shape == null) { |
9 | if (type.equals(ShapeType.OVAL_FILL)) { |
10 | shape = new Oval(true); |
11 | } else if (type.equals(ShapeType.OVAL_NOFILL)) { |
12 | shape = new Oval(false); |
13 | } else if (type.equals(ShapeType.LINE)) { |
14 | shape = new Line(); |
15 | } |
16 | shapes.put(type, shape); |
17 | } |
18 | return shape; |
19 | } |
20 | |
21 | public static enum ShapeType{ |
22 | OVAL_FILL, OVAL_NOFILL, LINE; |
23 | } |
24 | } |
- Client 类
1 | public class Client { |
2 | |
3 | public static void main(String[] args) { |
4 | Shape shape = ShapeFactory.getShape(ShapeType.LINE); |
5 | // 伪代码 |
6 | shape.draw(x1, y1, x2, y2); |
7 | |
8 | Shape shape1 = ShapeFactory.getShape(ShapeType.LINE); |
9 | // 伪代码 |
10 | shape1.draw(x1, y1, x2, y2); |
11 | } |
12 | } |
当我们多次创建 Line 图形时,由于程序使用了共享库,因此程序将快速执行。
小结
当我们需要创建一个类的许多对象且没有足够的内存容量时,可以使用享元模式。由于每个对象都会占用至关重要的内存空间,因此可以应用享元模式来通过共享对象来减少内存负载,同时也能提高程序效率。
事实上,享元模式可以避免大量非常相似类的开销。在程序设计中,有时需要生成大量细粒度的类实例来表示数据。如果能发现这些实例除了几个参数外基本上都是相同的,有时就能够受大幅度地减少需要实例化的类的数量。如果能把那些参数移到类实例的外面,在方法调用时将它们传递进来,就可以通过共享大幅度地减少单个实例的数目。