享元模式
原始代码
public class Website {
private String name;
public Website() {
}
public Website(String name) {
this.name=name;
}
public void use() {
System.out.println("网站分类: "+name);
}
public static void main(String[]args) {
Website ws=new Website("产品展示");
ws.use();
Website ws2=new Website("产品展示");
ws2.use();
Website ws3=new Website("产品展示");
ws3.use();
Website ws4=new Website("博客");
ws4.use();
Website ws5=new Website("博客");
ws5.use();
Website ws6=new Website("博客");
ws6.use();
}
}
问题描述:如果要做三个产品展示,三个博客的网站,就需要六个网站的实例,而其实它们本质上都是一样的代码,如果网站增多,实例也跟着增多,这对资源浪费的很严重.
享元模式
public abstract class Website {
public abstract void use();
}
public class ConcreteWebsite extends Website {
private String name;
public ConcreteWebsite(String name) {
this.name =name;
}
@Override
public void use() {
System.out.println("网站分类: "+name);
}
}
//用来创建并管理website对象,它主要用来确保合理地共享website
public class WebsiteFactory {
private HashMap<String,ConcreteWebsite>map=new HashMap<String,ConcreteWebsite>();
//获取网站分类
public Website getWebsiteCategory(String key) {
if(!map.containsKey(key)) {
map.put(key,new ConcreteWebsite(key));
}
return map.get(key);
}
//获取网站分类个数
public int getWebsiteCount() {
return map.size();
}
}
public class Demo {
public static void main(String[]args) {
WebsiteFactory wf=new WebsiteFactory();
Website ws =wf.getWebsiteCategory("产品展示");
ws.use();
Website ws2=wf.getWebsiteCategory("产品展示");
ws2.use();
Website ws3=wf.getWebsiteCategory("产品展示");
ws3.use();
Website ws4=wf.getWebsiteCategory("博客");
ws4.use();
Website ws5=wf.getWebsiteCategory("博客");
ws5.use();
Website ws6=wf.getWebsiteCategory("博客");
ws6.use();
System.out.println("实例化个数为"+wf.getWebsiteCount());
}
}
输出结果为:
网站分类: 产品展示
网站分类: 产品展示
网站分类: 产品展示
网站分类: 博客
网站分类: 博客
网站分类: 博客
实例化个数为2
问题描述:
上面的代码基本上实现了享元模式共享对象的目的,也就是说,不管建几个网站,只要”产品展示”都是一样的,只要是”博客”,都是完全相同的;但是这样是有问题的,你给企业建的网站不是一家企业的,它们的数据不会相同,所以至少它们都应该有不同的账号.(上面的代码只体现了它们共享的部分,却没有体现对象间的不同)
内部状态和外部状态
在享元对象内部并且不会随对象的改变而改变的共享部分,可以称为享元对象的内部状态,而随环境的改变而改变的,不可以共享的状态就是外部状态了.享元模式可以避免大量非常相似类的开销.在程序设计中,有时需要生成大量细粒度的类实例来表示数据.如果能发现这些实例除了几个参数外基本上都是相同的有时就能够受大幅度地减少需要实例化的类的数量.如果能把那些参数移到类实例的外面,在方法调用时将它们传递进来,就可以通过共享大幅度地减少单个实例的数目.
//用户类,用于网站的客户账号,是网站类的外部状态
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;
}
}
public abstract class Website {
public abstract void use(User user);
}
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());
}
}
//用来创建并管理website对象,它主要用来确保合理地共享website
public class WebsiteFactory {
private HashMap<String,ConcreteWebsite>map=new HashMap<String,ConcreteWebsite>();
//获取网站分类
public Website getWebsiteCategory(String key) {
if(!map.containsKey(key)) {
map.put(key,new ConcreteWebsite(key));
}
return map.get(key);
}
//获取网站分类个数
public int getWebsiteCount() {
return map.size();
}
}
public class Demo {
public static void main(String[]args) {
WebsiteFactory wf=new WebsiteFactory();
Website ws =wf.getWebsiteCategory("产品展示");
ws.use(new User("小明"));
Website ws2=wf.getWebsiteCategory("产品展示");
ws2.use(new User("小刚"));
Website ws3=wf.getWebsiteCategory("产品展示");
ws3.use(new User("小红"));
Website ws4=wf.getWebsiteCategory("博客");
ws4.use(new User("小兰"));
Website ws5=wf.getWebsiteCategory("博客");
ws5.use(new User("小杨"));
Website ws6=wf.getWebsiteCategory("博客");
ws6.use(new User("小文"));
System.out.println("实例化个数为"+wf.getWebsiteCount());
}
}
何时使用享元模式?
①如果一个应用程序使用了大量的对象,而大量的对象造成了很大的存储开销时就应该考虑使用
②对象的大多数状态可以外部状态,如果删除对象的外部状态,那么可以用相对较少的共享对象取代组对象
享元模式的好处?
因为有了享元模式,所以有了享元对象,实例总数大大减少,如果共享的对象越多,存储节约就越多,节约量随着共享状态的增多而增大.
享元模式的缺点:
享元模式需要维护一个记录了系统已有的所有享元的列表,而这本身需要消耗资源,另外享元模式使得系统更加复杂(为了使对象可以共享,需要将一些状态外部化,这使得程序的逻辑复杂化).因此,应当在足够多的对象实例可供共享时才值得使用享元模式.