本咸鱼开始修炼内功了,加油,奥利给!!!
问题引出
如果我们做一个小型外包网站项目,有的客户想以新闻的形式发布,有的客户想以博客的形式发布,有的客户想以微信公众号的形式发布,我们该怎么办?
传统解决方案
直接复制一份原网站,然后根据不同的需求进行修改,同时给每个网站租用一个空间
享元模式
简介
-
享元模式(Flyweight Pattern)也称蝇量模式
-
享元模式能够解决重复对象的内存浪费问题,当系统中有大量相似对象,需要缓冲池时,不需要总是创建新对象,而是从缓冲池里拿,这样可以降低系统内存、提高效率
-
享元模式是池技术的重要实现方式,经典应用场景有String常量连接池、数据库连接池、缓冲池等
注:享元模式是为数不多的只为提升系统性能而生的设计模式,他的主要作用是复用大对象(重量级对象),以节省内存空间和对象创建时间
外部状态和内部状态
-
内部状态指对象共享出来的信息,存储在享元对象内部且不会随环境的改变而改变(例如棋子的颜色)
-
外部状态指对象得以依赖的一个标记,是随环境改变而改变的、不可共享的状态(例如棋子的位置)
-
内部状态存储于ConcreteFlyweight对象(实现类)之中,而外部对象则应该考虑由客户端对象存储或计算。当调用Flyweight对象的操作时,将该状态传递给它。
为什么要使用享元模式
-
假如1000人看一场围棋直播,棋盘上有300个棋子,如果不使用享元模式,每一个棋子就是一个实例,每个观看的人都有300个棋子的实例,那么就会有300000个棋子,这对服务器内存占用是非常大的
-
使用享元模式,实例就会减少到两个(一个黑棋子对象,一个白棋子对象),棋子颜色是棋子的内部状态可共享,棋子位置是外部状态不可共享
享元模式实例
使用享元模式解决前面提出的网站外包问题
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;
}
@Override
public String toString() {
return 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("网站发布形式为:" + type + " , " + user +" 正在使用中...");
}
}
// 网站工厂类,根据需要返回一个网站(享元模式的核心)
public class WebSiteFactory {
// 集合,充当池的作业
private HashMap<String, ConcreteWebSite> pool = new HashMap<>();
// 根据网站的类型,返回一个网站,如果没有就创建一个网站,并放入池中,并返回
public WebSite getWebSiteCategory(String type) {
if (!pool.containsKey(type)) {
// 创建一个网站并放入池中
pool.put(type, new ConcreteWebSite(type));
}
return (WebSite)pool.get(type);
}
// 获取网站分类总数(池中有多少网站类型)
public int getWebSiteCount() {
return pool.size();
}
}
// 客户端,使用享元模式的组件
public class Client {
public static void main(String[] args) {
// 创建一个工厂
WebSiteFactory factory = new WebSiteFactory();
// 客户1要一个以新闻形式发布的网站
WebSite webSite1 = factory.getWebSiteCategory("新闻");
webSite1.use(new User("客户1"));
// 客户2要一个以博客形式发布的网站
WebSite webSite2 = factory.getWebSiteCategory("博客");
webSite2.use(new User("客户2"));
// 客户3要一个以博客形式发布的网站
WebSite webSite3 = factory.getWebSiteCategory("博客");
webSite3.use(new User("客户3"));
System.out.println("网站分类共:" + factory.getWebSiteCount() + "个");
}
}
// 运行结果
网站发布形式为:新闻 , 客户1 正在使用中...
网站发布形式为:博客 , 客户2 正在使用中...
网站发布形式为:博客 , 客户3 正在使用中...
网站分类共:2个
结果分析
虽然有三个客户定制了网站,但其中有两个客户的需求是一样的,第一次创建的 “博客” 分类被复用,所以池中只有两个网站分类