模式说明
某些对象相似度很高,只有少量字段或方法不同,当需要大量这类对象时,若为每一次需求都创建实例,内存开销会很大。此时可以提炼出这些对象的相同部分,作为所谓的享元,创建一个享元工厂类,持有享元实例。需要使用上述对象时,从享元工厂中获取该实例。若这些对象有不同部分,提取这些不同部分单独成类,使用时作为享元中的方法参数传入享元(使相同和不同部分结合)。这样无论使用多少次,对于享元而言,只有一个实例的开销。
本示例以网站为抽象享元,博客网站和电子商务网站为具体享元,用户为外部非享元。展示如何通过享元工厂类添加和获取享元,如何将非享元作为享元方法的参数传入到享元内。
结构
抽象享元类
被使用对象中提炼出的相同内容的集合,定义享元的抽象方法。
具体享元类
继承抽象享元类,实现抽象方法。
非享元类
需要使用但不能被提炼为享元内容的部分,单独成类,需要时传入享元。
享元工厂类
持有具体享元实例,有添加享元和获取享元的方法。
代码演示
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);}@Overridepublic 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);}@Overridepublic 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;}}
