结构型模式——享元模式(Flyweight Pattern)

享元模式(Flyweight Pattern)

应用

使用模板(Java)
复合享元模式
public class FlyweightPattern {
    public static void main(String[] args) {
        //创建两个外部状态
        UnsharedClass unsharedClassA = new UnsharedClass("外部操作A");
        UnsharedClass unsharedClassB = new UnsharedClass("外部操作B");
        //获取内部状态X的具体享元,并通过该享元执行外部操作A
        Flyweight flyweightX = FlyweightFactory.getFlyweight("X");
        flyweightX.operate(unsharedClassA);
        //获取内部状态Y的具体享元,并通过该享元执行外部操作A
        Flyweight flyweightY = FlyweightFactory.getFlyweight("Y");
        flyweightY.operate(unsharedClassA);
        //获取内部状态Z的具体享元,并通过该享元执行外部操作B
        Flyweight flyweightZ = FlyweightFactory.getFlyweight("Z");
        flyweightZ.operate(unsharedClassB);
        //获取内部状态X的具体享元,并通过该享元执行外部操作A
        Flyweight flyweightReX = FlyweightFactory.getFlyweight("X");
        flyweightReX.operate(unsharedClassA);
    }
}
//抽象享元角色,在此处设置独立的享元角色抽象类
abstract class Flyweight {
    //内部状态
    public String intrinsic;
    public Flyweight(String intrinsic) {
        this.intrinsic = intrinsic;
    }
    //要求享元角色必须接受外部状态
    public abstract void operate(UnsharedClass unsharedClass);
}
//具体的享元角色
class ConcreteFlyweight extends Flyweight {
    //确定内部状态,并使用该状态构件相应的享元对象
    public ConcreteFlyweight(String intrinsic) {
        super(intrinsic);
    }
    //通过传入外部对象来实现具体的操作
    @Override
    public void operate(UnsharedClass unsharedClass) {
        System.out.println("外部操作:");
        unsharedClass.operate();
        System.out.println("内部操作" + intrinsic);
        System.out.println("========================");
    }
}
//非享元角色,即变动不会影响享元角色的部分
class UnsharedClass {
    public String param;
    public UnsharedClass(String param) {
        this.param = param;
    }
    public void operate() {
        System.out.println(param);
    }
}
//享元工厂角色
class FlyweightFactory {
    //定义一个池容器
    private static HashMap<String, Flyweight> pool = new HashMap<>();
    //静态方法,用来根据条件返回对应的享元对象
    public static Flyweight getFlyweight(String intrinsic) {
        Flyweight flyweight = null;
        //池中是否有该对象,如果有
        if (pool.containsKey(intrinsic)) {    
            //从池中获取
            flyweight = pool.get(intrinsic);
            System.out.print("已有 " + intrinsic + " 直接从池中取---->");
        } else {
            //根据外部状态创建享元对象
            flyweight = new ConcreteFlyweight(intrinsic);
            //放入池中
            pool.put(intrinsic, flyweight);
            System.out.print("创建 " + intrinsic + " 并从池中取出---->");
        }
        //返回享元角色
        return flyweight;
    }
}
单纯享元模式
public class FlyweightPattern {
    public static void main(String[] args) {
        //获取内部状态X的具体享元
        Flyweight flyweightX = FlyweightFactory.getFlyweight("X");
        flyweightX.operate();
        //获取内部状态Y的具体享元
        Flyweight flyweightY = FlyweightFactory.getFlyweight("Y");
        flyweightY.operate();
        //获取内部状态Z的具体享元
        Flyweight flyweightZ = FlyweightFactory.getFlyweight("Z");
        flyweightZ.operate();
        //获取内部状态X的具体享元
        Flyweight flyweightReX = FlyweightFactory.getFlyweight("X");
        flyweightReX.operate();
    }
}
abstract class Flyweight {
    public String intrinsic;
    public Flyweight(String intrinsic) {
        this.intrinsic = intrinsic;
    }
    //享元角色无须接受外部状态
    public abstract void operate();
}
class ConcreteFlyweight extends Flyweight {
    public ConcreteFlyweight(String intrinsic) {
        super(intrinsic);
    }
    @Override
    public void operate( ) {
       System.out.println("内部操作" + intrinsic);
    }
}
//享元工厂角色
class FlyweightFactory {
    //定义一个池容器
    private static HashMap<String, Flyweight> pool = new HashMap<>();
    //静态方法,用来根据条件返回对应的享元对象
    public static Flyweight getFlyweight(String intrinsic) {
        Flyweight flyweight = null;
        //池中是否有该对象,如果有
        if (pool.containsKey(intrinsic)) {    
            //从池中获取
            flyweight = pool.get(intrinsic);
            System.out.print("已有 " + intrinsic + " 直接从池中取---->");
        } else {
            //根据外部状态创建享元对象
            flyweight = new ConcreteFlyweight(intrinsic);
            //放入池中
            pool.put(intrinsic, flyweight);
            System.out.print("创建 " + intrinsic + " 并从池中取出---->");
        }
        //返回享元角色
        return flyweight;
    }
}

类似于懒汉式的枚举,在使用的时候创建对应的对象。

应用实例
  • Integer
  • 连接池技术
  • 本质上Redis也是一种享元模式

简介

享元模式(Flyweight Pattern)又称轻量级模式属于结构型模式。主要用于减少创建对象的数量,以减少内存占用和提高性能。这种类型的设计模式属于,它提供了减少对象数量从而改善应用所需的对象结构的方式。

考虑系统性能的设计模式,通过使用享元模式可以节约内存空间,提高系统的性能

模式分类:
  • 单纯享元模式(即该对象没有外部状态,整个对象可以共享,类似于单例模式)
  • 复合享元模式(该对象有内蕴状态,也有外蕴状态,内蕴状态共享)
UML
  • 单纯享元模式

单纯享元模式

  • 复合享元模式

复合享元模式

角色
  • 享元工厂(FlyweightFactory)角色:用于创建具体享元类,维护相同的享元对象。当请求对象已经存在时,直接返回对象,不存在时,在创建对象。
  • 抽象享元(Flyweight)角色:定义需要共享的对象业务接口。享元类被创建出来总是为了实现某些特定的业务逻辑.
  • 具体享元(ConcreteFlyweight)角色:实现抽象享元类的接口,完成某一具体逻辑。在这里表示可以被借出。
  • 非享元(Unsharable Flyweight)角色:是不可以共享的外部状态,它以参数的形式注入具体享元的相关方法中**(复合享元模式)**。
前提解释:

享元模式的本质: 分离变与不变,并将不变共享

  1. 内部状态(Intrinsic State):在享元模式中可以共享的相同内容。
  2. 外部状态(Extrinsic State):那些需要外部环境来设置的不能共享的内容。
  3. 其中外部状态和内部状态是相互独立的。
    • 内部状态在各个对象间共享
  4. 外部状态由客户端传入。
    • 外部状态的变化不会引起内部状态的变化。
意图:

在实际使用中,能够共享的内部状态是有限的,因此享元对象一般都设计为较小的对象,它所包含的内部状态较少,这种对象也称为 细粒度对象

  • 运用共享技术支持大量细粒度的对象的复用。
主要解决:
  • 在创建出大量相同或相似对象实例时,有可能造成过多的资源消耗。
何时使用:
  1. 系统中有大量对象。
  2. 这些对象消耗大量内存。
  3. 这些对象的状态大部分可以外部化。
  4. 这些对象可以按照内蕴状态分为很多组,当把外蕴对象从对象中剔除出来时,每一组对象都可以用一个对象来代替。
  5. 系统不依赖于这些对象身份,这些对象是不可分辨的。
如何解决:
  • 享元工厂(FlyweightFactory)是享元模式的核心,它需要确保系统可以共享相同的对象。
    • 它会维护一个对象列表,当我们想要获取享元类时,如果请求的享元类已经被创建,则直接返回已有的享元类
    • 若没有被创建,则创建一个新的享元对象,并将它加入到维护队列中。
应用实例:
  1. JAVA 中的 String,如果有则返回,如果没有则创建一个字符串保存在字符串缓存池里面。
  2. 数据库的数据池。
优点:
  • 降低系统的内存,使效率提高。
缺点:
  • 提高了设计难度,需要在设计阶段便进行合理的配置。
使用场景:
  • 系统有大量相似对象。
  • 需要缓冲池的场景。
注意事项:

1、注意划分外部状态和内部状态,否则可能会引起线程安全问题。 2、这些类必须有一个工厂对象加以控制。

JDK使用分析

类Integer

JDK中的Integer中使用了该模式

Integer部分UML图

Integer与享元模式

角色对应
  • **享元工厂(FlyweightFactory)角色:**方法Integer.valueOf(int i)与类IntegerCache结合。
  • **抽象享元(Flyweight)角色:**无,该工厂直接对应了具体享元。
  • **具体享元(ConcreteFlyweight)角色:**包含不同值的Integer实例。
代码分析
public final class Integer extends Number implements Comparable<Integer> { 
    /**
     * 与缓存类结合扮演了工厂角色,此处为获得实例
     * Returns an {@code Integer} instance representing the specified
     * {@code int} value.  If a new {@code Integer} instance is not
     * required, this method should generally be used in preference to
     * the constructor {@link #Integer(int)}, as this method is likely
     * to yield significantly better space and time performance by
     * caching frequently requested values.
     * 返回一个整数实例,该实例表示指定的 int 值。如果不需要新的 Integer 实例,
     * 则此方法通常应优先于构造函数 Integer(int)使用,因为此方法可能通过缓存
     * 经常请求的值来产生明显更好的空间和时间性能。此方法将始终缓存 -128 
     * 到 127(包括 )范围内的值,并可能缓存此范围之外的其他值。
     */
    public static Integer valueOf(int i) {
        //判断是否存在于缓存中的值(-128~127)
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            //从缓存中获取
            return IntegerCache.cache[i + (-IntegerCache.low)];
        //新建实例
        return new Integer(i);
    }
    /**
     *  与方法valueOf(int i)结合扮演了工厂角色,此处为存储实例
     */
    private static class IntegerCache {
        static final int low = -128;
        static final int high;
        static final Integer cache[];
        //静态代码块加载最大值到最小值的缓存
        static {
            // high value may be configured by property
            int h = 127;
            String integerCacheHighPropValue =
                sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
            if (integerCacheHighPropValue != null) {
                try {
                    int i = parseInt(integerCacheHighPropValue);
                    i = Math.max(i, 127);
                    // Maximum array size is Integer.MAX_VALUE
                    h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
                } catch( NumberFormatException nfe) {
                    // If the property cannot be parsed into an int, ignore it.
                }
            }
            high = h;
            cache = new Integer[(high - low) + 1];
            int j = low;
            for(int k = 0; k < cache.length; k++)
                cache[k] = new Integer(j++);

            // range [-128, 127] must be interned (JLS7 5.1.7)
            assert IntegerCache.high >= 127;
        }
        private IntegerCache() {}
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值