享元模式

享元模式(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
    @Override
4
    public void operation(int status) {
5
        System.out.println("具体的 Flyweight: " + status);
6
    }
7
}
  • UnshareConcreteFlyweight 类
1
public class UnshareConcreteFlyweight implements Flyweight {
2
3
    @Override
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
	@Override
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
	@Override
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 图形时,由于程序使用了共享库,因此程序将快速执行。

小结

当我们需要创建一个类的许多对象且没有足够的内存容量时,可以使用享元模式。由于每个对象都会占用至关重要的内存空间,因此可以应用享元模式来通过共享对象来减少内存负载,同时也能提高程序效率。

事实上,享元模式可以避免大量非常相似类的开销。在程序设计中,有时需要生成大量细粒度的类实例来表示数据。如果能发现这些实例除了几个参数外基本上都是相同的,有时就能够受大幅度地减少需要实例化的类的数量。如果能把那些参数移到类实例的外面,在方法调用时将它们传递进来,就可以通过共享大幅度地减少单个实例的数目。