大家好,我是馆长!今天开始我们讲的是结构型模式中的享元模式。老规矩,讲解之前再次熟悉下结构型模式包含:代理模式、适配器模式、桥接模式、装饰器模式、外观模式、享元模式、组合模式,共7种设计模式。
享元模式(Flyweight Pattern)
定义
享元(Flyweight)模式的定义:运用共享技术来有效地支持大量细粒度对象的复用。它通过共享已经存在的对象来大幅度减少需要创建的对象数量、避免大量相似类的开销,从而提高系统资源的利用率。解决创建大量相同或相似对象实例的问题。
解决问题
创建大量相同或相似对象实例的问题。创建那么多的对象将会耗费很多的系统资源,它是系统性能提高的一个瓶颈,在有大量对象时,有可能会造成内存溢出,如果把它们相同的部分提取出来共享,则能节省大量的系统资源,我们把其中共同的部分抽象出来,如果有相同的业务请求,直接返回在内存中已有的对象,避免重新创建。
实现
内部状态:即不会随着环境的改变而改变的可共享部分。
外部状态:指随环境改变而改变的不可以共享的部分。
享元模式的实现要领就是区分应用中的这两种状态,并将外部状态外部化。下面来分析其基本结构和实现方法。
结构
主要角色:
抽象享元角色(Flyweight):是所有的具体享元类的基类,为具体享元规范需要实现的公共接口,非享元的外部状态以参数的形式通过方法传入。
具体享元(Concrete Flyweight)角色:实现抽象享元角色中所规定的接口。
非享元(Unsharable Flyweight)角色:是不可以共享的外部状态,它以参数的形式注入具体享元的相关方法中。
享元工厂(Flyweight Factory)角色:负责创建和管理享元角色。当客户对象请求一个享元对象时,享元工厂检查系统中是否存在符合要求的享元对象,如果存在则提供给客户;如果不存在的话,则创建一个新的享元对象。
应用场景:
系统中存在大量相同或相似的对象,这些对象耗费大量的内存资源。
大部分的对象可以按照内部状态进行分组,且可将不同部分外部化,这样每一个组只需保存一个内部状态【数据分离形态】。
需要缓冲池的场景。
注意:
注意划分外部状态和内部状态,否则可能会引起线程安全问题。
这些类必须有一个工厂对象加以控制,控制对象的维护和获取,以及其他的业务设定。
由于享元模式需要额外维护一个保存享元的数据结构,所以应当在有足够多的享元实例时才值得使用享元模式。
优点:
相同对象只要保存一份,这降低了系统中对象的数量,从而降低了系统中细粒度对象给内存带来的压力。
大大减少对象的创建,降低系统的内存,使效率提高。
缺点:
为了使对象可以共享,需要将一些不能共享的状态外部化,这将增加程序的复杂性。
读取享元模式的外部状态会使得运行时间稍微变长。
代码实现:模拟在坐标系中绘制图形。
//抽象享元角色:Marker
public interface Maker {
void draw(Coordinate coordinate);
}
//具体享元角色: CircleMaker
@Data
public class CircleMaker implements Maker {
@Override
public void draw(Coordinate coordinate) {
System.out.println("在X:"+coordinate.getX()+"Y:"+coordinate.getY()+"为圆点,绘制圆形");
}
}
//具体享元角色: RectangleMaker
@Data
public class RectangleMaker implements Maker {
@Override
public void draw(Coordinate coordinate) {
System.out.println("在X:"+coordinate.getX()+"Y:"+coordinate.getY()+"为中心点,绘制矩形");
}
}
//非享元角色:Coordinate
@Data
public class Coordinate {
private Double x;
private Double y;
Coordinate() {
}
Coordinate(Double x, Double y) {
this.x = x;
this.y = y;
}
}
//限定获取结构:只能获取这些类型
public enum MakerType {
Circle("CircleMaker"),Rectangle("RectangleMaker");
private String name;
MakerType(String name){
this.name=name;
}
}
//享元工厂角色:MakerFactory
@Data
public class MakerFactory {
private Map pool = new HashMap();
//初始化
public static MakerFactory builder() {
return new MakerFactory();
}
private MakerFactory() {
}
public Maker get(MakerType type) {
if (pool.get(type) == null) {
System.out.println(type.name()+"被创建了");
switch (type) {
case Circle:
set(type, new CircleMaker());
break;
case Rectangle:
set(type, new RectangleMaker());
break;
default:
}
}else{
System.out.println(type.name()+"已创建,无需创建");
}
return pool.get(type);
}
private void set(MakerType type, Maker maker) {
pool.put(type, maker);
}
}
//模拟客户端:ClientDemo
public class ClientDemo {
public static void main(String[] args) {
MakerFactory makerFactory = MakerFactory.builder();
Maker c1 = makerFactory.get(MakerType.Circle);
c1.draw(new Coordinate(1.0, 3.0));
System.out.println("\n");
Maker c2 = makerFactory.get(MakerType.Circle);
c2.draw(new Coordinate(2.0, 4.0));
System.out.println("\n");
Maker r1 = makerFactory.get(MakerType.Rectangle);
r1.draw(new Coordinate(10.0, 30.0));
System.out.println("\n");
Maker r2 = makerFactory.get(MakerType.Rectangle);
r2.draw(new Coordinate(120.0, 130.0));
}
}
扩展
在实际使用过程中,有时候会稍加改变,即存在两种特殊的享元模式:单纯享元模式和复合享元模式:
(1)单纯享元模式:这种享元模式中的所有的具体享元类都是可以共享的,不存在非共享的具体享元类。
(2)复合享元模式:这种享元模式中的有些享元对象是由一些单纯享元对象组合而成的,它们就是复合享元对象。虽然复合享元对象本身不能共享,但它们可以分解成单纯享元对象再被共享。
好了,关于享元模式的说明,馆长就先讲到这里。谢谢各位看官!!
23 种设计模式不是孤立存在的,很多模式之间存在一定的关联关系,在大的系统开发中常常同时使用多种设计模式,或者模式与模式之间的组合进行生成更加强大的程序功能。