享元模式

1 展示网站的项目需求

我们给客户做了一个产品展示的网站,客户的朋友觉得效果不错,也希望我们帮他做一个这样的网站,但是要求有些不同,希望网站以新闻的形式发布,但是有些朋友希望以博客的形式发布,还有一些朋友希望以微信公众号的形式发布。面对这样一个需求我们应该如何处理呢?
通常的做法是直接复制粘贴一份,然后根据客户的不同要求进行定制修改,给每一个网站租用一个空间。但是这种解决方法有一个问题:创建出的这些网站相似度很高,而且都不是高访问量的网站,如果分成多个虚拟空间来处理,相当于一个相同的网站的实例对象很多,造成服务器资源的浪费。
解决这个问题也很简单,就是将其整合到一个网站中,共享相关代码和数据,对硬盘、内存、CPU、数据库空间等服务器资源都可以达成共享,减少服务器资源的浪费。

2 什么是享元模式

享元模式(Flyweight Pattern)也叫蝇量模式,运用共享技术有效地支持大量细粒度的对象,常用于系统底层开发,解决系统性能问题。像数据库链接池,里面都是创建好的连接对象,在这些连接对象中有我们需要的则直接拿来用,避免重新创建。享元模式能够解决重复对象的内存浪费问题,它的经典实用场景就是池技术,像String常量池、Integer常量池、数据库连接池等,都是利用了享元模式。
角色分析:
1)Flyweight:抽象的享元角色,它是产品的抽象类,同时定义出对象的外部状态和内部状态的接口或实现。内部状态是指存储在享元对象内部且不会随环境的改变而改变。外部状态是对象依赖的一个标记,是随环境而改变的,是不可共享的状态。
2)ConcreteFlyweight:具体的享元角色,也就是具体额产品类,实现抽象角色定义的相关业务。
3)UnsharedConcreteFlyweight:不可共享的角色,通常不会出现在享元工厂中。
4)FlyweightFactory:享元工厂类,用于构建一个池容器(集合),同时提供从池中获取对象的方法。


享元模式的类图

3 代码实现享元模式

首先有一个抽象的享元角色,网站的抽象类:

public abstract class WebSite {
    public abstract void use(User user);
}

网站的具体实现类ConcreteWebSite:

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

享元工厂类WebSiteFactory:

/**
 * 网站工厂类,根据需要返回一个网站实例
 */
public class WebSiteFactory {
    // 集合,充当池的作用
    private HashMap<String, WebSite> 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 factory = new WebSiteFactory();

        // 客户以新闻形式发布网站
        WebSite webSite1 = factory.getWebSiteCategory("新闻");
        webSite1.use(new User("张三"));

        // 客户以博客形式发布网站
        WebSite webSite2 = factory.getWebSiteCategory("博客");
        webSite2.use(new User("李四"));

        // 客户以博客形式发布网站
        WebSite webSite3 = factory.getWebSiteCategory("博客");
        webSite3.use(new User("王五"));

        // 客户以博客形式发布网站
        WebSite webSite4 = factory.getWebSiteCategory("博客");
        webSite4.use(new User("赵六"));

        // 网站总数
        System.out.println("网站分类总数: " + factory.getWebSiteCount());
    }
}

输出结果:

网站的发布形式为:新闻, 使用者:张三
网站的发布形式为:博客, 使用者:李四
网站的发布形式为:博客, 使用者:王五
网站的发布形式为:博客, 使用者:赵六
网站分类总数: 2

可以看到,虽然网站使用了4次,但是发布形式只有两种,实际创建出的网站对象只有两个。


网站需求的类图

4 享元模式的特点和注意事项

1)当系统中有大量对象,这些对象消耗大量内存,并且对象的状态大部分可以外部化时,可以考虑使用享元模式。
2)享元模式提高了系统的复杂度,需要分离出内部状态和外部状态,而外部状态具有固化特性,不应该随着内部状态的改变而改变。
3)使用享元模式时,要注意划分内部状态和外部状态,并且需要有一个工厂类加以控制。
4)享元模式的经典应用场景是需要缓冲池的场景,如String常量池、数据库连接池等。

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容