提升软件性能的秘密武器:揭秘实战中的享元模式

什么是享元模式

享元模式是一种对象结构型模式,享元模式通过存储这些共享实例对象的地方称为享元池(Flyweight Pool),可以避免频繁的创建销毁对象,大幅度减少需要创建的对象数量,避免大量相似类的开销,从而提高系统资源的利用率。一般和单例模式配合使用,将享元工厂声明为一个单例类来池化享元对象。享元模式要求能够共享的对象必须是细粒度对象,因此它又称为轻量级模式。

享元模式的核心角色

享元模式的主要核心角色有抽象享元角色(AbstractFlyWeight)、具体享元(ConcreteFlyWeight)角色、享元工厂(FlyWeightFactory)角色和客户端角色。

  1. 抽象享元角色(AbstractFlyWeight):为具体享元角色规定了必须实现的方法,在Java中可以是抽象类,也可以是接口。
  2. 具体享元角色(ConcreteFlyWeight):实现抽象角色定义的方法。
  3. 享元工厂角色(FlyWeightFactory):生成AbstractFlyWeight角色的工厂,在工厂中生成的AbstractFlyWeight角色可以实现共享实例。享元工厂提供一个用于存储享元对象的享元池,用户需要对象时,首先从享元池中获取,如果享元池中不存在,则创建一个新的享元对象返回给用户,并在享元池中保存该新增对象。
  4. 客户端角色(Client):使用FlyWeightFactory来生成AbstractFlyWeight角色。

享元模式的基本原理

享元模式的核心思想是分离出不变部分与可变部分,通过共享不变部分来减少对象的创建,从而降低内存消耗和提高性能。这种模式的实现方式是通过将对象的状态分为内部状态和外部状态,内部状态是对象不可变的共享部分,而外部状态是可以改变的独立部分。当多个客户端请求同一个享元对象时,它们实际上共享的是同一个对象,这样就避免了重复创建相同的对象,从而节省了内存空间。同时,享元模式还通过工厂模式来实现对象的创建和管理,通过缓存等技术手段来提高对象的复用效率。

享元模式如何实现

活字印刷术是中国古代的四大发明之一,每次想要执行印刷任务的时候,先去已经刻好的字模中选择,如果能够找到相应的字模,就直接取出进行排版,如果找不到,就麻烦一点了,需要刻出该字模,当然这样的麻烦只需要一次,下次再用到该字模的时候就不需要这么麻烦了。如果用Java写一个程序实现这个过程,需要怎么实现呢?可以使用享元模式来实现,实现步骤:

1、定义抽象享元类:ChineseCharacter.java。

java 复制代码
/**
 * 汉字抽象类
 */
public interface ChineseCharacter {
    /**
     * 汉字
     */
    String val();
}

2、定义具体享元类:Fan.java、 Fu.java、 Bian.java、 Cheng.java。

java 复制代码
public class Fan implements ChineseCharacter{

    @Override
    public String val() {
        return "凡";
    }
}
java 复制代码
public class Fu implements ChineseCharacter{

    @Override
    public String val() {
        return "夫";
    }
}
java 复制代码
public class Bian implements ChineseCharacter{
    @Override
    public String val() {
        return "编";
    }
}
java 复制代码
public class Cheng implements ChineseCharacter{

    @Override
    public String val() {
        return "程";
    }
}

3、定义享元工厂类:ChineseCharacterFactory.java

java 复制代码
public class ChineseCharacterFactory {
    private static Map<String, ChineseCharacter> chineseCharacterMap = new ConcurrentHashMap<>();
    private static final ChineseCharacterFactory LIBRARY_FACTORY = new ChineseCharacterFactory();

    public static ChineseCharacterFactory getInstance() {
        return LIBRARY_FACTORY;
    }
    @SneakyThrows
    public ChineseCharacter get(Class clazz) {
        ChineseCharacter chineseCharacter;
        if (chineseCharacterMap.containsKey(clazz.getName())) {
            chineseCharacter = chineseCharacterMap.get(clazz.getName());
            System.out.println("取字:'" + chineseCharacter.val() + "'");
        } else {
            Constructor constructor = clazz.getConstructor();
            Object obj = constructor.newInstance();
            chineseCharacter = (ChineseCharacter) obj;
            System.out.println("刻字:'" + chineseCharacter.val() + "'");
            chineseCharacterMap.put(clazz.getName(), chineseCharacter);
        }
        return chineseCharacter;
    }
}

4、定义客户端角色(Client):Test.java

java 复制代码
public class Test {
    public static void main(String[] args) {
        System.out.println("开启活字印刷");
        for (int i = 0; i < 3; i++) {
            System.out.println("---------"+(i+1)+"---------");
            ChineseCharacterFactory chineseCharacterFactory = ChineseCharacterFactory.getInstance();
            ChineseCharacter fan = chineseCharacterFactory.get(Fan.class);
            ChineseCharacter fu = chineseCharacterFactory.get(Fu.class);
            ChineseCharacter bian = chineseCharacterFactory.get(Bian.class);
            ChineseCharacter cheng = chineseCharacterFactory.get(Cheng.class);
            System.out.println(fan.val() + fu.val() + bian.val() + cheng.val());
        }
    }
}

在这个例子中,我们通过享元模式将已经刻好的汉字对象缓存起来,避免了重复创建相同的数字对象。当需要执行印刷任务的时候,首先从缓存中查找是否已经创建过该汉字对象,如果存在则直接使用,否则创建一个新的对象并缓存起来。这样,我们可以通过共享相同的数字对象来优化内存消耗,提高印刷效率,这也是活字印刷术区别于一般印刷术的核心所在。

享元模式适用哪些场景

  1. 需要大量使用相同或相似对象的场景。享元模式通过共享对象来避免重复创建相同的对象,从而降低内存消耗和提高系统性能。
  2. 需要频繁创建和销毁同一类对象的情况。享元模式通过缓存对象来避免重复创建相同对象,从而降低系统开销和响应时间。

对于性能要求较高、需要降低内存消耗的系统,享元模式是一种有效的解决方案。

享元模式有哪些实际案例

  1. 数据库连接池:在许多应用程序中,需要使用数据库连接来进行数据访问。为了避免频繁地创建和关闭数据库连接,可以使用享元模式来缓存已经创建的连接对象。当客户端需要使用数据库连接时,首先从连接池中获取可用的连接对象,如果连接池中没有可用的对象,则创建一个新的连接对象并加入到连接池中。通过这种方式,可以避免频繁地创建和关闭数据库连接,提高系统的效率和性能。
  2. 线程池:线程池是另一种常见的享元模式应用场景。线程池可以避免频繁地创建和销毁线程对象,从而提高系统的效率和性能。线程池的实现方式与数据库连接池类似,通过缓存预先创建的线程对象,避免重复创建相同的线程对象,从而提高系统的效率和性能。
  3. 图形界面设计:在图形界面设计中,享元模式可以用于实现组件的复用和缓存。例如,在一个Web应用程序中,可以使用享元模式来缓存已经创建的按钮对象,避免重复创建相同的按钮对象。通过这种方式,可以提高界面的渲染效率和性能。

享元模式与单例模式的区别

  1. 目的:单例模式的目的是确保一个类仅有一个实例,而享元模式的目的是通过共享对象来减少内存消耗,提高程序的性能。
  2. 实现方式:单例模式通常使用静态变量或枚举类型来实现,而享元模式则通过定义内部状态和外在状态来实现共享。
  3. 共享方式:单例模式仅有一个实例,而享元模式可以有多个共享对象。
  4. 内存占用:单例模式仅有一个对象实例,而享元模式可以有多个共享对象,可以节省内存空间。
  5. 应用场景:单例模式适用于需要确保一个类仅有一个实例的场景,例如日志记录器、配置管理器等,而享元模式适用于需要大量使用对象的场景,例如图形界面、网络通信等。

总结:享元模式和单例模式都是重要的设计模式,它们的目的和应用场景不同,但都可以提高程序的性能和可维护性。

享元模式总结

优点:

  1. 减少内存消耗:通过共享对象,避免创建大量重复的对象实例,从而减少内存消耗。
  2. 提高性能:由于对象被共享,因此可以减少对象的创建和销毁,从而提高系统的性能。
  3. 外部状态与内部状态的分离:享元模式将对象的外部状态和内部状态分离,使得对象的状态更加灵活和可维护。

缺点:

  1. 复杂度较高:享元模式需要分离出内部状态和外部状态,这使得程序的逻辑变得复杂。
  2. 运行时间可能变长:为了使对象可以共享,享元模式需要将享元对象的部分状态外部化,而读取外部状态将使得运行时间变长。
  3. 需要维护享元池:享元模式需要维护一个享元池来管理共享对象,这可能会引入额外的复杂性。

总之,享元模式适用于那些需要大量使用相同或相似对象,并且需要频繁创建和销毁同一类对象的场景。通过共享对象来避免重复创建相同的对象,从而提高系统性能和降低内存消耗,但是享元模式也存在一些缺点,需要根据具体情况权衡其优缺点,考虑是否适合使用享元模式。

相关推荐
ok!ko1 小时前
设计模式之原型模式(通俗易懂--代码辅助理解【Java版】)
java·设计模式·原型模式
2401_857622661 小时前
SpringBoot框架下校园资料库的构建与优化
spring boot·后端·php
2402_857589361 小时前
“衣依”服装销售平台:Spring Boot框架的设计与实现
java·spring boot·后端
吾爱星辰2 小时前
Kotlin 处理字符串和正则表达式(二十一)
java·开发语言·jvm·正则表达式·kotlin
拉里小猪的迷弟2 小时前
设计模式-创建型-常用:单例模式、工厂模式、建造者模式
单例模式·设计模式·建造者模式·工厂模式
哎呦没2 小时前
大学生就业招聘:Spring Boot系统的架构分析
java·spring boot·后端
_.Switch3 小时前
Python Web 应用中的 API 网关集成与优化
开发语言·前端·后端·python·架构·log4j
编程、小哥哥3 小时前
netty之Netty与SpringBoot整合
java·spring boot·spring
IT学长编程4 小时前
计算机毕业设计 玩具租赁系统的设计与实现 Java实战项目 附源码+文档+视频讲解
java·spring boot·毕业设计·课程设计·毕业论文·计算机毕业设计选题·玩具租赁系统
莹雨潇潇4 小时前
Docker 快速入门(Ubuntu版)
java·前端·docker·容器