设计模式——享元模式(蝇量模式)

本文介绍了享元模式的基本概念及其在解决系统性能问题方面的应用。通过一个网站产品展示的例子,展示了如何利用享元模式减少服务器资源的浪费,提高代码的可维护性和扩展性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

开始介绍之前,先分析一个实例:

        现有一个小型的外包项目,给客户A做一个产品展示网站,客户A的朋友感觉效果不错,也希望做这样的产品展示网站,但是要求有些不同:

        ·有客户要求以新闻的形式发布

        ·有客户要求以微博的形式发布

        ·有客户希望以微信公众号的形式发布

传统解决方案:直接复制一份,然后根据不同的客户需求进行定制修改。给每个网站分配一个虚拟空间。

对上述解决方案进行分析:

(1) 需要网站结构的相似度很高,而且都不是高访问量网站,如果分成多个虚拟空间来处理,相当于一个相同网站的实例对象很多,造成服务器的资源浪费。

(2)解决思路:整合到一个网站中,共享其相关的代码数据,对于硬盘、内存、CPU、数据库空间等服务器资源都可以达成共享,减少服务器资源。即使用享元模式进行修改。这样对于代码来说,由于是一份实例,维护和扩展都更加容易。

基本介绍

(1)享元模式也叫蝇量模式:运用共享技术有效地支持大量细粒度的对象。

(2)常用于系统底层的开发,解决系统的性能问题。像数据库连接池,里面都是创建好的连接对象,在这些连接对象中有我们需要的则直接拿来用,避免重新创建,如果没有我们需要的,则创建一个。

(3)享元模式可以解决重复对象的内存浪费问题,当系统中有大量相似的对象,需要缓冲池时,不需要总是创建新对象,可以从缓冲池里拿。这样可以降低系统内存,同时提高效率。

(4)享元模式经典的应用场景便是池技术,String常量池,数据库连接池,缓冲池等等都是享元模式的应用,享元模式是池技术的重要实现方式。

原理图:

 

原理图说明:

(1)Flyweight:抽象的享元角色,它是产品的抽象类,同时定义出对象的外部状态和内部状态的接口或者实现。

(2)ConcreteFlyweight:具体的享元角色,是具体的产品类,实现抽象角色定义的相关业务。

(3)UnsharedConcreteFlyweight:不可共享的角色,一般不会出现在享元工厂FlyweightFactory。

(4)FlyweightFactory:享元工厂,用于构建一个池容器(集合),同时提供获取对象的相关方法。

内部对象和外部对象:

内部对象:指对象共享出来的信息,存储在享元对象内部且不会随环境的改变而改变。

外部对象:指对象得以依赖的一个标记,是随环境改变而改变的、不可共享的状态。

比如:围棋棋子只有黑白两种颜色,各个棋子的位置不同,棋子的颜色是固定的,但是位置是变化的,因此棋子颜色就是棋子的内部状态,棋子坐标就是棋子外部状态。

实例分析

上述实例使用享元模式实现:

// 外部状态
public class User {
    private String name;

    public User(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}


// 抽象的网站角色
public abstract class WebSite {
    public abstract void use(User user);
}

// 具体的网站角色
public class ConcreteWebSite extends WebSite {
    // 共享的对象,内部状态
    private String type = ""; // 网站发布类型

    public ConcreteWebSite(String type) {
        this.type = type;
    }

    @Override
    public void use(User user) {
        System.out.println(user.getName() + "要求网站的发布形式为:" + type);
    }
}

// 网站工厂, 根据需求返回一个具体的网站
public class WebSiteFactory {
    private HashMap<String, ConcreteWebSite> webSitePool = new HashMap<>();
    // 根据网站类型返回一个网站,如果没有就创建一个网站,并放入到池中
    public WebSite getWebSiteCategory(String type) {
        if (!webSitePool.containsKey(type)) {
            webSitePool.put(type, new ConcreteWebSite(type));
        }
        return webSitePool.get(type);
    }

    // 获取网站分类的总数
    public int getWebSiteCount() {
        return webSitePool.size();
    }

}


// 主方法
public static void main(String[] args) {
    WebSiteFactory webSiteFactory = new WebSiteFactory();
    WebSite webSite1 = webSiteFactory.getWebSiteCategory("新闻");
    webSite1.use(new User("用户1"));
    WebSite webSite2 = webSiteFactory.getWebSiteCategory("微博");
    webSite2.use(new User("用户2"));
    WebSite webSite3 = webSiteFactory.getWebSiteCategory("新闻");
    webSite3.use(new User("用户3"));
    System.out.println(webSiteFactory.getWebSiteCount());
    /**
      * 用户1要求网站的发布形式为:新闻
      * 用户2要求网站的发布形式为:微博
      * 用户3要求网站的发布形式为:新闻
      * 网站类型2
    * */
}

总结

(1)Integer中的valueOf方法使用了享元模式。如果Integer.valueOf(x) x在-128~127之间,便是用享元模式返回。(具体见源码)

Integer x = Integer.valueOf(127);
Integer y = Integer.valueOf(127);
System.out.println(x == y); // true
        
        
Integer z = Integer.valueOf(128);
Integer w = Integer.valueOf(128);
System.out.println(z == w); // false

(2)系统中有大量对象,这些对象消耗大量内存,并且对象的状态大部分可以外部化时,就可以考虑使用享元模式。

(3)享元模式大大减少了对象的创建,降低了程序内存的占用,提高效率。

(4)享元模式提高了系统的复杂度。需要分离出内部状态和外部状态,而外部状态具有固化特性,不应该随着内部状态的改变而改变。

(5)使用享元模式,需划分内部状态和外部状态,并且需要一个工厂类加以控制。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值