设计模式之享元模式

设计模式之享元模式_第1张图片

 

什么是享元模式

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

享元模式的核心角色

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

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

设计模式之享元模式_第2张图片

享元模式的基本原理

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

享元模式如何实现

设计模式之享元模式_第3张图片

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

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

/**
 * 汉字抽象类
 */
public interface ChineseCharacter {
    /**
     * 汉字
     */
    String val();
}

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

public class Fan implements ChineseCharacter{

    @Override
    public String val() {
        return "凡";
    }
}
public class Fu implements ChineseCharacter{

    @Override
    public String val() {
        return "夫";
    }
}
public class Bian implements ChineseCharacter{
    @Override
    public String val() {
        return "编";
    }
}
public class Cheng implements ChineseCharacter{

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

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

public class ChineseCharacterFactory {
    private static Map 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

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());
        }
    }
}

设计模式之享元模式_第4张图片

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

享元模式适用哪些场景

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

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

享元模式有哪些实际案例

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

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

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

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

享元模式总结

优点:

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

缺点:

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

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

你可能感兴趣的:(设计模式,设计模式,享元模式,java,uml,后端)