注:示例来自《大话设计模式》
假如现有如下需求 有多个客户想要使用同样的网站 但要求不太一样 有的希望是博客形式的 还有的希望是产品图片说明形式的 简单代码实现如下
网站类
package Test26;
//网站
public class WebSite {
private String name = "";
public WebSite(String name)
{
this.name = name;
}
public void Use()
{
System.out.println("网站分类:" + name);
}
}
客户端代码
package Test26;
public class Program {
public static void main(String[] args) {
WebSite fx = new WebSite("产品展示");
fx.Use();
WebSite fy = new WebSite("产品展示");
fy.Use();
WebSite fz = new WebSite("产品展示");
fz.Use();
WebSite fl = new WebSite("博客");
fl.Use();
WebSite fm = new WebSite("博客");
fm.Use();
WebSite fn = new WebSite("博客");
fn.Use();
}
}
上面的写法 如果要做三个产品展示 三个博客的网站 就需要六个网站类的实例 而其实它们本质上都是一样的代码 如果网站增多 实例也就随着增多 这对服务器的资源浪费得很严重 下面我们使用享元模式进行重构 代码如下
网站抽象类
package Test26;
//网站
public abstract class WebSite {
public abstract void Use();
}
具体网站类
package Test26;
//具体的网站
public class ConcreteWebSite extends WebSite {
private String name = "";
public ConcreteWebSite(String name)
{
this.name = name;
}
@Override
public void Use() {
System.out.println("网站分类:" + name);
}
}
网站工厂类
package Test26;
import java.util.Hashtable;
//网站工厂
public class WebSiteFactory {
private Hashtable flyweights = new Hashtable();
//获得网站分类
public WebSite GetWebSiteCategory(String key)
{
if (!flyweights.containsKey(key))
flyweights.put(key, new ConcreteWebSite(key));
return ((WebSite)flyweights.get(key));
}
//获得网站分类总数
public int GetWebSiteCount()
{
return flyweights.size();
}
}
客户端代码
package Test26;
public class Program {
public static void main(String[] args) {
WebSiteFactory f = new WebSiteFactory();
WebSite fx = f.GetWebSiteCategory("产品展示");
fx.Use();
WebSite fy = f.GetWebSiteCategory("产品展示");
fy.Use();
WebSite fz = f.GetWebSiteCategory("产品展示");
fz.Use();
WebSite fl = f.GetWebSiteCategory("博客");
fl.Use();
WebSite fm = f.GetWebSiteCategory("博客");
fm.Use();
WebSite fn = f.GetWebSiteCategory("博客");
fn.Use();
System.out.println("网站分类总数为 "+f.GetWebSiteCount());
}
}
这样写算是基本实现了享元模式的共享对象的目的 但这样是有问题的 多个用户使用同一个网站 它们的数据不会相同 所以至少它们都应该有不同的账号 下面我们加入用户进行重构 代码如下
用户类
package Test26;
//用户
public class User {
private String name;
public User(String name)
{
this.name = name;
}
public String getName() {
return name;
}
}
网站抽象类
package Test26;
//网站
public abstract class WebSite {
public abstract void Use(User user);
}
具体网站类
package Test26;
//具体的网站
public class ConcreteWebSite extends WebSite {
private String name = "";
public ConcreteWebSite(String name)
{
this.name = name;
}
@Override
public void Use(User user) {
System.out.println("网站分类:" + name + " 用户:" + user.getName());
}
}
网站工厂类
package Test26;
import java.util.Hashtable;
//网站工厂
public class WebSiteFactory {
private Hashtable flyweights = new Hashtable();
//获得网站分类
public WebSite GetWebSiteCategory(String key)
{
if (!flyweights.containsKey(key))
flyweights.put(key, new ConcreteWebSite(key));
return ((WebSite)flyweights.get(key));
}
//获得网站分类总数
public int GetWebSiteCount()
{
return flyweights.size();
}
}
客户端代码
package Test26;
public class Program {
public static void main(String[] args) {
WebSiteFactory f = new WebSiteFactory();
WebSite fx = f.GetWebSiteCategory("产品展示");
fx.Use(new User("小菜"));
WebSite fy = f.GetWebSiteCategory("产品展示");
fy.Use(new User("大鸟"));
WebSite fz = f.GetWebSiteCategory("产品展示");
fz.Use(new User("娇娇"));
WebSite fl = f.GetWebSiteCategory("博客");
fl.Use(new User("老顽童"));
WebSite fm = f.GetWebSiteCategory("博客");
fm.Use(new User("桃谷六仙"));
WebSite fn = f.GetWebSiteCategory("博客");
fn.Use(new User("南海鳄神"));
System.out.println("得到网站分类总数为 "+f.GetWebSiteCount());
}
}
享元模式 运用共享技术有效地支持大量细粒度的对象
享元模式可以避免大量非常相似类的开销 在程序设计中 有时需要生成大量细粒度的类实例来表示数据 如果能发现这些实例除了几个参数外基本都是相同的 有时就能够受大幅度地减少需要实例化的类的数量 如果能把那些参数移到类实例的外面 在方法调用时将它们传递进来 就可以通过共享大幅度地减少单个实例的数目
如果一个应用程序使用了大量的对象 而大量的这些对象造成了很大的存储开销时就应该考虑使用享元模式 还有就是对象的大多数状态可以外部状态 如果删除对象的外部状态 那么可以用相对较少的共享对象取代很多组对象 此时可以考虑使用享元模式
缺点 使用享元模式需要维护一个记录了系统已有的所有享元的列表 而这本身需要耗费资源 另外享元模式使得系统更加复杂 为了使对象可以共享 需要将一些状态外部化 这使得程序的逻辑复杂化