设计模式:014_享元模式

74 阅读3分钟

享元模式

享元模式(Flyweight Pattern),运用共享技术有效地支持大量细粒度的对象。

作用

  • 极大减少内存中相似或相同对象数量,节约系统资源,提供系统性能。
  • 提高效率,由于创建对象的数量减少,所以对系统内存的需求也减小,使得速度更快,效率更高。
  • 享元模式中的外部状态相对独立,且不影响内部状态。

主要目的是实现对象的共享,即共享池,用于减少创建对象的数量,以减少内存占用和提高性能。 享元模式尝试重用现有的同类对象,如果未找到匹配的对象,则创建新对象。

缺点

为了使对象可以共享,需要将享元对象的部分状态外部化,分离内部状态和外部状态,使程序逻辑复杂

细粒度/内部状态/外部状态

  • 细粒度:较小的对象,所包含的内部状态较小
  • 内部状态:不随环境改变而改变的状态,内部状态可以共享
  • 外部状态:随着环境改变而改变的状态,不可以共享的状态,享元对象的外部状态通常由客户端保存,并在享元对象创建后,需要的时候传入享元对象内部,不同的外部状态是相互独立的。

适用场景

  • 系统有大量相似对象,比如 JAVA 中的 String
  • 需要缓冲池的场景,比如数据库的数据连接池
  • 对象大部分状态都可以外部化
  • 剥离出对象的外部状态后,可以使用相对较少的共享对象取代大量对象
  • 常常应用于系统底层的开发,以便解决系统的性能问题

类图

享元模式类图.png

  • Flyweight(抽象享元类):通常是接口或抽象类,抽象享元类中声明了具体享元类公共方法,这些方法可以向外界提供享元对象的内部数据(内部状态),同时也可以通过这些方法来设置外部数据(外部状态)
  • ConcreteFlyweight(具体享元类):继承抽象享元类,在具体享元类中为内部状态提供存储空间。通常可以结合单例模式来设计具体享元类,为每一个具体享元类提供唯一的享元对象
  • UnshareConcreteFlyweight(非分享具体享元类):并不是所有的具体享元类都需要被共享,不能被共享的子类可以设计为非共享具体享元类,当需要一个非共享具体享元类的对象时可以直接通过实例化创建,一般不会出现在享元工厂中
  • FlyweightFactory(享元工厂类):创建并管理享元对象,将各种具体享元类存储到一个享元池中,享元池一般为“键值对”集合,可以结合工厂模式进行设计。当用户请求一个具体享元对象时,享元池中如果保存的有就直接返回给用户,如果没有就创建该享元对象返回给用户并存储到享元池中

代码表达

// Flyweight(抽象享元类)
public abstract class WebSite {
    public abstract void use(User user);
}
// ConcreteFlyweight(具体享元类)
public class ConcreteWebSite extends WebSite {
    // 共享的部分;属于内部状态
    private String type = "";

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

    // User 不是共享的部分;属于外部状态
    @Override
    public void use(User user) {
        System.out.println("ConcreteWebSite type:" + type + " by user:" + user.getName());
    }
}
// UnshareConcreteFlyweight(非分享具体享元类)
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;
    }
}
// FlyweightFactory(享元工厂类)
public class WebSiteFactory {
    private HashMap<String, ConcreteWebSite> pool = new HashMap<>();

    public WebSite getWebSite(String key) {
        if (!pool.containsKey(key)) {
            pool.put(key, new ConcreteWebSite(key));
        }
        return pool.get(key);
    }

    public int getWebSiteCount() {
        return pool.size();
    }
}

客户端调用

WebSiteFactory factory = new WebSiteFactory();
WebSite webSite1 = factory.getWebSite("weibo");
webSite1.use(new User("Tom"));
WebSite webSite2 = factory.getWebSite("weibo");
webSite2.use(new User("Bob"));
WebSite webSite3 = factory.getWebSite("weibo");
webSite3.use(new User("Andy"));
WebSite webSite4 = factory.getWebSite("weixin");
webSite4.use(new User("Jhon"));
System.out.println("getWebSiteCount:" + factory.getWebSiteCount());