设计模式:享元模式解决重复对象的内存浪费问题

一、定义

“享元”指被共享的单元。享元模式就是通过复用对象,以达到节省内存的目的。

尝试复用现有的同类对象,如果未找到匹配的对象,则创建新对象。

意图:运用共享技术有效地支持大量细粒度的对象。

主要解决:在有大量对象时,有可能会造成内存溢出,我们把其中共同的部分抽象出来,如果有相同的业务请求,直接返回在内存中已有的对象,避免重新创建。

二、内部状态和外部状态

享元模式提出了两个要求: 细粒度和共享对象。这里涉及到内部状态和外部状态,即将对象的信息分为两个部分:内部状态和外部状态。

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

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

以围棋为例,围棋只有黑白二色,其颜色不会随环境改变而改变,所以围棋颜色就是围棋的内部状态,可共享;棋子之间的差别在于位置不同,而落子后,棋子的位置会发生变化,所以棋子的坐标是棋子的外部状态,不可共享。

三、享元模式原理类图

(1)抽象享元角色(Flyweight):是所有的具体享元类的基类,为具体享元规范需要实现的公共接口,非享元的外部状态以参数的形式通过方法传入。

(2)具体享元角色(Concrete Flyweight):实现抽象享元角色中所规定的接口。

(3)非享元角色(Unsharable Flyweight) :是不可以共享的外部状态,它以参数的形式注入具体享元的相关方法中。

(4)享元工厂(Flyweight Factory)角色:负责创建和管理享元角色。当客户对象请求一个享元对象时,享元工厂检查系统中是否存在符合要求的享元对象,如果存在则提供给客户;如果不存在的话,则创建一个新的享元对象。

四、案例

以网站项目展示为例,有的要求以新闻形式发布,有的要求以博客形式发布,有的要求以微信公众号形式发布。

整合到一个网站中,共享其相关的代码和数据,对于硬盘、内存、CPU、数据库空间等服务器资源都可以达成共享,减少服务器资源。

//抽象享元角色
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("网站的发布形式为:"+type+","+user.getName()+"在使用");
    }
}

//非享元角色
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 class WebSiteFactory {
    //集合,充当池的作用
    private HashMap pool = new HashMap<>();
 
    //根据网站类型,返回一个网站,如果没有就创建一个并放入池中
    public WebSite getWebSiteCategory(String type){
        if(!pool.containsKey(type)){
            pool.put(type,new ConcreteWebSite(type));
        }
        return pool.get(type);
    }
 
    //获取网站分类数
    public int getWebSiteCount(){
        return pool.size();
    }
 
}

//客户端
public class Client {
    public static void main(String[] args) {
        //创建一个工程类
        WebSiteFactory webSiteFactory = new WebSiteFactory();
 
        //A以一个新闻形式发布网站
        WebSite newsWebsite = webSiteFactory.getWebSiteCategory("新闻");
        newsWebsite.use(new User("A"));
        //B以一个博客形式发布网站
        WebSite blogWebsite1 = webSiteFactory.getWebSiteCategory("博客");
        blogWebsite1.use(new User("B"));
        //C以一个博客形式发布网站
        WebSite blogWebsite2 = webSiteFactory.getWebSiteCategory("博客");
        blogWebsite2.use(new User("C"));
        //D以一个博客形式发布网站
        WebSite blogWebsite3 = webSiteFactory.getWebSiteCategory("博客");
        blogWebsite3.use(new User("D"));
 
        //实际网站发布分类实例
        System.out.println("实际网站发布分类实例:"+webSiteFactory.getWebSiteCount());
 
    }
}

五、应用

在Java Integer的实现中,-128到127之间的整型对象会被事先创建好,缓存在IntegerCache类中。当我们使用自动装箱或者valueOf()来创建这个数值区间的整型对象时,会复用IntegerCache类事先创建好的对象。这里的IntegerCache类就是享元工厂类,事先创建好的整型对象就是享元对象。

在Java String类的实现中,JVM开辟一块存储区专门存储字符串常量,这块存储区叫作字符串常量池,类似于Integer中的IntegerCache。不过,跟IntegerCache不同的是,它并非事先创建好需要共享的对象,而是在程序的运行期间,根据需要来创建和缓存字符串常量。

展开阅读全文

页面更新:2024-02-22

标签:对象   常量   棋子   围棋   抽象   角色   形式   内存   状态   模式   网站   博客

1 2 3 4 5

上滑加载更多 ↓
推荐阅读:
友情链接:
更多:

本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828  

© CopyRight 2020-2024 All Rights Reserved. Powered By 71396.com 闽ICP备11008920号-4
闽公网安备35020302034903号

Top