模式说明
某些对象相似度很高,只有少量字段或方法不同,当需要大量这类对象时,若为每一次需求都创建实例,内存开销会很大。此时可以提炼出这些对象的相同部分,作为所谓的享元,创建一个享元工厂类,持有享元实例。需要使用上述对象时,从享元工厂中获取该实例。若这些对象有不同部分,提取这些不同部分单独成类,使用时作为享元中的方法参数传入享元(使相同和不同部分结合)。这样无论使用多少次,对于享元而言,只有一个实例的开销。
本示例以网站为抽象享元,博客网站和电子商务网站为具体享元,用户为外部非享元。展示如何通过享元工厂类添加和获取享元,如何将非享元作为享元方法的参数传入到享元内。
结构
抽象享元类
被使用对象中提炼出的相同内容的集合,定义享元的抽象方法。
具体享元类
继承抽象享元类,实现抽象方法。
非享元类
需要使用但不能被提炼为享元内容的部分,单独成类,需要时传入享元。
享元工厂类
持有具体享元实例,有添加享元和获取享元的方法。
代码演示
package com.yukiyama.pattern.structure;
import java.util.HashMap;
import java.util.Map;
/**
* 享元模式
*/
public class FlyweightDemo {
public static void main(String[] args) {
// 声明享元工厂
WebsiteFactory fa = new WebsiteFactory();
// 声明具体享元博客网站和电子商务网站
Website blog = new BlogWebsite("Blog");
Website ec = new BlogWebsite("EC");
// 向享元工厂中添加如上两种享元
fa.addFlyweight(blog);
fa.addFlyweight(ec);
// 通过享元工厂,创建两个博客网站和两个电子商务网站
Website blog1 = fa.getFlyweight("Blog");
Website blog2 = fa.getFlyweight("Blog");
Website ec1 = fa.getFlyweight("EC");
Website ec2 = fa.getFlyweight("EC");
// 将两个博客网站和两个电子商务网站分给不同的使用者
blog1.use(new User("莫小言"));
blog2.use(new User("金大庸"));
ec1.use(new User("马风"));
ec2.use(new User("刘强西"));
// 如下均输出“true”,即通过享元工厂获取的多个享元以及
// 工厂内持有的享元均为同一个。
System.out.println(blog == blog1 && blog1 == blog2);
System.out.println(ec == ec1 && ec1 == ec2);
}
}
/**
* 享元工厂类
* 以Map数据结构持有享元,key为网站类型,value为Website实例。
* 实现添加享元,获取享元的方法。
* 客户端声明享元工厂后,需要继续创建享元并将其添加进享元工厂中。
*/
class WebsiteFactory{
private Map<String, Website> flyweights = new HashMap<>();
public void addFlyweight(Website web) {
if(!flyweights.containsKey(web.getCatagory())) {
flyweights.put(web.getCatagory(), web);
} else {
System.out.println("已存在该享元。");
}
}
public Website getFlyweight(String key) {
if(!flyweights.containsKey(key)) {
System.out.println("无此享元。");
return null;
} else {
return flyweights.get(key);
}
}
}
/**
* 享元抽象类
* 声明享元的字段和相关方法。
*/
abstract class Website{
private String catagory;
public Website(String catagory) {
this.catagory = catagory;
}
public String getCatagory() {
return catagory;
}
public abstract void use(User user);
}
/**
* 具体享元类
* 继承享元抽象类,实现抽象方法。
* 如下是博客网站类。
*/
class BlogWebsite extends Website{
public BlogWebsite(String catagory) {
super(catagory);
}
@Override
public void use(User user) {
System.out.printf("这是一个%s网站,提供文章发布服务。\n", getCatagory());
System.out.printf("网站用户为%s。\n", user.getUser());
}
}
/**
* 具体享元类
* 如下是电子商务网站类。
*/
class ECWebsite extends Website{
public ECWebsite(String catagory) {
super(catagory);
}
@Override
public void use(User user) {
System.out.printf("这是一个%s网站,提供商品发布服务。\n", getCatagory());
System.out.printf("网站用户为%s。\n", user.getUser());
}
}
/**
* 非享元类
* 享元Website需要结合非享元User使用,例如对博客网站来说,他们可能共用
* 相同的文章编辑器控件(作为享元的一部分),但各自使用的用户不同(非享元)。
*/
class User{
private String user;
public User(String user) {
this.user = user;
}
public String getUser() {
return user;
}
}