文章目录
- 享元模式
- 1.享元模式的本质
- 2.何时选用享元模式
- 3.优缺点
- 4.享元模式的结构
- 5.实现
- 最初实现
- 享元模式初步改造
- 享元模式再改进
- 享元模式再优化
享元模式
享元模式最开始看就是类似缓存,缓存一些信息,节约查询时间,以空间换时间
但是再理解后才发现他的好处,他可以细粒化对象,抽离内部状态和外部状态来优化缓存
1.享元模式的本质
享元模式的本质:分离与共享。
分离的是对象状态中变与不变的部分,共享的是对象中不变的部分。享元模式的关键之处就在于分离变与不变,把不变的部分作为享元对象的内部状态,而变化部分则作为外部状态,由外部来维护,这样享元对象就能够被共享,从而减少对象数量,并节省大量的内存空间。
2.何时选用享元模式
- 如果一个应用程序使用了大量的细粒度对象,可以使用享元模式来减少对象数量。
- 如果由于使用大量的对象,造成很大的存储开销,可以使用享元模式来减少对象数量,并节约内存。
- 如果不考虑对象的外部状态,可以用相对较少的共享对象取代很多组合对象,可以使用享元模式来共享对象,然后组合对象来使用这些共享对象。
- 如果不考虑对象的外部状态,可以用相对较少的共享对象取代很多组合对象,可以使用享元模式来共享对象,然后组合对象来使用这些共享对象。
3.优缺点
享元模式的优点是:减少对象数量,节省内存空间。
- 可能有的朋友认为共享对象会浪费空间,但是如果这些对象频繁使用,那么其实是·节省空间的。因为占用空间的大小等于每个对象实例占用的大小再乘以数量,对于享元对象来讲,基本上就只有一个实例,大大减少了享元对象的数量,并节省不少的内存空间。
节省的空间取决于以下几个因素:因为共享而减少的实例数目、每个实例本身所占用的空间。假如每个对象实例占用2个字节,如果不共享数量是100个,而共享后就只有一个了,那么节省的空间约等于(100-1)×2字节。
享元模式的缺点是:维护共享对象,需要额外开销。
- 如同前面演示的享元工厂,在维护共享对象的时候,如果功能复杂,会有很多额外的开销,比如有一个线程来维护垃圾回收。
4.享元模式的结构
- Flyweight:享元接口,通过这个接口 Flyweight可以接受并作用于外部状态。通过这个接口传入外部的状态,在享元对象的方法处理中可能会使用这些外部的数据。
- ConcreteFlyweight:具体的享元实现对象,必须是可共享的,需要封装 Flyweight的内部状态。
- UnsharedConcreteFlyweight:非共享的享元实现对象,并不是所有的 Flyweight实现对象都需要共享。非共享的享元实现对象通常是对共享享元对象的组合对象。
- FlyweightFactory:享元工厂,主要用来创建并管理共享的享元对象,并对外提供访问共享享元的接口。
- Client:享元客户端,主要的工作是维持一个对Flyweight的引用,计算或存储享元对象的外部状态,当然这里可以访问共享和不共享的Flyweight对象。
5.实现
现有6个客户想各自做一个网站,整体结构相同,但归属不同客户
-
有的客户希望是新闻发布形式的
-
有的客户希望是博客形式的
-
有的客户希望是公众号形式的等等
最初实现
1.网站类
/**
* @description:网站类
*/
@AllArgsConstructor
public class WebSite {
private String name = "";
public void init() {
System.out.println("当前网站分类: " + name);
}
}
2.测试类
public class Test {
public static void main(String[] args) {
WebSite webSite1 = new WebSite("博客");
webSite1.init();
WebSite webSite2 = new WebSite("博客");
webSite2.init();
WebSite webSite3 = new WebSite("博客");
webSite3.init();
WebSite webSite4 = new WebSite("新闻发布");
webSite4.init();
WebSite webSite5 = new WebSite("公众号");
webSite5.init();
WebSite webSite6 = new WebSite("公众号");
webSite6.init();
}
}
3.结果
享元模式初步改造
1.网站接口及其实现类(也可不要接口)
/**
* @description:网站接口
*/
public interface WebSite {
/**
* 初始化网站
*/
void init();
}
/**
* @description:网站实现类
*/
@AllArgsConstructor
public class ConcreteWebSite implements WebSite {
/**
* 网站类型
*/
private String type;
@Override
public void init() {
System.out.println("当前网站分类: " + type);
}
}
2.网站工厂类
/**
* @description:网站工厂
*/
public class WebSiteFactory {
/**
* 缓存网站对象
*/
private Map<String, WebSite> map = new HashMap<>();
/**
* 获取网站对象
* @param type 网站类型
* @return
*/
public WebSite getWebSiteCategory(String type) {
//存在这个类型的网站就直接返回,不存在就新建放入map中缓存起来
if (map.get(type) == null) {
map.put(type, new ConcreteWebSite(type));
}
return map.get(type);
}
/**
* 统计缓存的网站对象数量
* @return
*/
public Integer getWebSiteCount() {
return map.size();
}
}
3.测试类
public class Client {
public static void main(String[] args) {
// 创建一个工厂
WebSiteFactory factory = new WebSiteFactory();
// 给客户创建一个博客类型的网站
WebSite webSite1 = factory.getWebSiteCategory("博客");
webSite1.init();
// 给客户创建一个博客类型的网站
WebSite webSite2 = factory.getWebSiteCategory("博客");
webSite2.init();
// 给客户创建一个博客类型的网站
WebSite webSite3 = factory.getWebSiteCategory("博客");
webSite3.init();
// 给客户创建一个新闻发布类型的网站
WebSite webSite4 = factory.getWebSiteCategory("新闻发布");
webSite4.init();
// 给客户创建一个公众号类型的网站
WebSite webSite5 = factory.getWebSiteCategory("公众号");
webSite5.init();
// 给客户创建一个公众号类型的网站
WebSite webSite6 = factory.getWebSiteCategory("公众号");
webSite6.init();
// 查看实例数
System.out.println("实例数:" + factory.getWebSiteCount());
}
}
4.结果
可以看到只创建了3个对象,而最初的写法创建了6个对象,现在看可能提升不明显,但如果是需要创建100个对象,1000个对象呢?
享元模式再改进
上面创建三个博客类型的网站,但是好像这三个网站就是一模一样的,但是不同的客户,所以加上网站归属用户,再改造
1.接口不变
2.实现类增加客户名属性
/**
* @description:网站实现类
*/
@AllArgsConstructor
public class ConcreteWebSite implements WebSite {
/**
* 网站类型
*/
private String type;
/**
* 网站归属用户
*/
private String userName;
@Override
public void init() {
System.out.println("当前网站分类: " + type + " 【客户】: " + userName);
}
}
3.网站工厂类,现在有两个属性(网站类型、客户名),用他俩一起做键
/**
* @description:网站工厂
*/
public class WebSiteFactory {
/**
* 缓存网站对象
*/
private Map<String, WebSite> map = new HashMap<>();
/**
* 获取网站对象
* @param type 网站类型
* @return
*/
public WebSite getWebSiteCategory(String type, String userName) {
//用"客户:类型"做键
String key = userName + ":" + type;
//存在这个类型的网站就直接返回,不存在就新建放入map中缓存起来
if (map.get(key) == null) {
map.put(key, new ConcreteWebSite(type, userName));
}
return map.get(key);
}
/**
* 统计缓存的网站对象数量
* @return
*/
public Integer getWebSiteCount() {
return map.size();
}
}
4.测试类
public class Client {
public static void main(String[] args) {
// 创建一个工厂
WebSiteFactory factory = new WebSiteFactory();
// 给客户创建一个博客类型的网站
WebSite webSite1 = factory.getWebSiteCategory("博客", "客户A");
webSite1.init();
// 给客户创建一个博客类型的网站
WebSite webSite2 = factory.getWebSiteCategory("博客", "客户B");
webSite2.init();
// 给客户创建一个博客类型的网站
WebSite webSite3 = factory.getWebSiteCategory("博客", "客户C");
webSite3.init();
// 给客户创建一个新闻发布类型的网站
WebSite webSite4 = factory.getWebSiteCategory("新闻发布", "客户A");
webSite4.init();
// 给客户创建一个公众号类型的网站
WebSite webSite5 = factory.getWebSiteCategory("公众号", "客户B");
webSite5.init();
// 给客户创建一个公众号类型的网站
WebSite webSite6 = factory.getWebSiteCategory("公众号", "客户C");
webSite6.init();
// 查看实例数
System.out.println("实例数:" + factory.getWebSiteCount());
}
}
5.结果
可以看到又是6个对象了,那这样用不用设计模式都一样了
享元模式再优化
从上面结果可以发现,网站其实只有3种类型(博客、新闻发布、公众号),只是归属不同的客户,那么可以拆分,将网站类型作为内部状态,客户作为外部状态,再改造
-
内部状态:对象共享出来的信息,存储在享元对象内部并且不会随环境改变的共享部分
-
外部状态:对象用来标记的一个内容,随环境会改变,不可共享
1.改造网站接口及其实现类,在调用方法时传入客户名
/**
* @description:网站接口
*/
public interface WebSite {
/**
* 初始化网站传入客户名
* @param userName 客户名
*/
void init(String userName);
}
/**
* @description:网站实现类
*/
@AllArgsConstructor
public class ConcreteWebSite implements WebSite {
/**
* 网站类型
*/
private String type;
@Override
public void init(String userName) {
System.out.println("当前网站分类: " + type + " 【客户】: " + userName);
}
}
2.网站工厂类不变
/**
* @description:网站工厂
*/
public class WebSiteFactory {
/**
* 缓存网站对象
*/
private Map<String, WebSite> map = new HashMap<>();
/**
* 获取网站对象
* @param type 网站类型
* @return
*/
public WebSite getWebSiteCategory(String type) {
//存在这个类型的网站就直接返回,不存在就新建放入map中缓存起来
if (map.get(type) == null) {
map.put(type, new ConcreteWebSite(type));
}
return map.get(type);
}
/**
* 统计缓存的网站对象数量
* @return
*/
public Integer getWebSiteCount() {
return map.size();
}
}
3.测试类
public class Client {
public static void main(String[] args) {
// 创建一个工厂
WebSiteFactory factory = new WebSiteFactory();
// 给客户创建一个博客类型的网站
WebSite webSite1 = factory.getWebSiteCategory("博客");
webSite1.init("客户A");
// 给客户创建一个博客类型的网站
WebSite webSite2 = factory.getWebSiteCategory("博客");
webSite2.init("客户B");
// 给客户创建一个博客类型的网站
WebSite webSite3 = factory.getWebSiteCategory("博客");
webSite3.init("客户C");
// 给客户创建一个新闻发布类型的网站
WebSite webSite4 = factory.getWebSiteCategory("新闻发布");
webSite4.init("客户A");
// 给客户创建一个公众号类型的网站
WebSite webSite5 = factory.getWebSiteCategory("公众号");
webSite5.init("客户B");
// 给客户创建一个公众号类型的网站
WebSite webSite6 = factory.getWebSiteCategory("公众号");
webSite6.init("客户C");
// 查看实例数
System.out.println("实例数:" + factory.getWebSiteCount());
}
}
4.结果
这样将不变的信息作为内部状态,将不确定的信息作为外部状态,这就是享元模式的最终模式