享元模式(Flyweight):
为什么需要享元模式:软件系统采用纯粹对象方案的问题在于大量细粒度的对象充斥在系统中,而享元模式可以在避免大量细粒度对象对象的问题的同时,让外部客户程序仍然能够透明的使用面向对象的方式来进行操作。
1.问题引入:(多个用户使用同样的网站,但要求不同,有的博客形式有的产品展示形式)
package computer;
public class Test {
public static void main(String[] args) {
Website web1=new Website("产品展示");
web1.use();
Website web2=new Website("产品展示");
web2.use();
Website web3=new Website("产品展示");
web3.use();
Website web4=new Website("博客");
web4.use();
Website web5=new Website("博客");
web5.use();
Website web6=new Website("博客");
web6.use();
}
}
//网站类
class Website{
private String name="";
public Website(String name) {
this.name=name;
}
public void use() {
System.out.println("网站分类:"+name);
}
}
缺点:三个博客网页就要实例化三个对象,存储开销大。
2.解决:享元模式
关键:区分内部状态和外部状态。
内部状态:存储在享元对象内部不会随环境的改变而改变。可共享。
外部状态:随环境变化而变。不可共享。
享元对象的外部状态必须由客户端保存,并在享元对象被创建之后,在需要使用的时候再传入到享元对象内部。
package computer;
import java.util.HashMap;
public class Test {
public static void main(String[] args) {
FlyweightFactory f=new FlyweightFactory();
int extrinsic=1;
Flyweight f1=FlyweightFactory.getFlyweight("X");
f1.operate(extrinsic++);
Flyweight f2=FlyweightFactory.getFlyweight("Y");
f2.operate(extrinsic++);
Flyweight f3=FlyweightFactory.getFlyweight("Z");
f3.operate(extrinsic++);
Flyweight f4=FlyweightFactory.getFlyweight("X");
f4.operate(extrinsic++);
Flyweight unsharedFlyweight=new UnsharedConcreteFlyweight("M");
unsharedFlyweight.operate(extrinsic++);
}
}
abstract class Flyweight{
//内部状态
public String intrinsic;
//外部状态(不能二次改变)
protected final String extrinsic;
//构造中设置外部状态原因:享元角色必须要接受外部状态
public Flyweight(String extrinsic) {
this.extrinsic=extrinsic;
}
//定义业务操作(虚函数的原因:子类必须设置自己的业务)
public abstract void operate(int extrinsic);
//设置和获取内部状态的函数不是虚函数
public String getIntrinsic() {
return intrinsic;
}
public void setIntrinsic(String intrinsic) {
this.intrinsic = intrinsic;
}
}
//需要共享的Flyweight子类,为内部状态增加存储空间
class ConcreteFlyweight extends Flyweight{
//接受外部状态
public ConcreteFlyweight(String extrinsic) {
super(extrinsic);
}
//根据外部状态进行逻辑处理
public void operate(int extrinsic) {
System.out.println("具体Flyweight:"+extrinsic);
}
}
//不需要共享的Flyweight子类
class UnsharedConcreteFlyweight extends Flyweight{
//接受外部状态
public UnsharedConcreteFlyweight(String extrinsic) {
super(extrinsic);
}
//根据外部状态进行逻辑处理
public void operate(int extrinsic) {
System.out.println("不共享的具体Flyweight:"+extrinsic);
}
}
//享元工厂类:创建Flyweight对象(决定新创建一个还是返回一个已经存在的)
class FlyweightFactory{
//定义一个池容器(静态)
private static HashMap<String,Flyweight> pool=new HashMap<String,Flyweight>();
//享元工厂(静态)(实例化享元类的时候用到)
public static Flyweight getFlyweight(String extrinsic) {
Flyweight flyweight=null;
if(pool.containsKey(extrinsic)){
flyweight=pool.get(extrinsic);
System.out.print("已有"+extrinsic+"直接从池中取出---->");
}else {
flyweight=new ConcreteFlyweight(extrinsic);
pool.put(extrinsic, flyweight);
System.out.print("创建"+extrinsic+"并从池中取出---->");
}
return flyweight;
}
}
个别时候可能不需要共享,UnsgaredConcreteFlyweight类可以解决不需要共享对象的问题。
3.问题引入的改进:(无外部状态的享元模式)
package computer;
import java.util.HashMap;
public class Test {
public static void main(String[] args) {
WebsiteFactory f=new WebsiteFactory();
Website f1=f.getWebsite("产品展示");
f1.use();
Website f2=f.getWebsite("产品展示");
f2.use();
Website f3=f.getWebsite("产品展示");
f3.use();
Website fa=f.getWebsite("博客");
fa.use();
Website fb=f.getWebsite("博客");
fb.use();
Website fc=f.getWebsite("博客");
fc.use();
System.out.print("网站分类总数为:"+f.getWebsiteCount());
}
}
abstract class Website{
public abstract void use();
}
class ConcreteWebsite extends Website{
private String name="";
public ConcreteWebsite(String name) {
this.name=name;
}
public void use() {
System.out.println("网站分类:"+name);
}
}
class WebsiteFactory{
private static HashMap<String,Website> pool=new HashMap<String,Website>();
public static Website getWebsite(String name) {
Website website=null;
if(pool.containsKey(name)) {
website=pool.get(name);
System.out.println("非新创建网站:");
}else {
website=new ConcreteWebsite(name);
pool.put(name, website);
System.out.println("新创建网站:");
}
return website;
}
public int getWebsiteCount() {
return pool.size();
}
}
缺点:只要是“产品展示”网页,就都是一样的;只要是博客网页,就都是一样的。无法实现同类型(name)对象间的不同,如博客网页也应每个人有不同的账号。
4.改进:
改进的原因:生成大量的细粒度的类实例时,若发现这些实例除了参数结构外基本是相同的,则把这些参数移到类实例外面(单独成为一个类),在方法调用的时候将它们传进来,可以通过共享大幅度地减少单个实例地数目。
改进目标:外部状态由客户端对象存储或计算,当调用Website对象操作时,将该状态传递给它。
客户端的使用:用WebsiteFactory的getWebsite()方法可以创建网站并设定网站类型名,用具体website对象的use类可以创建该网站的账户。
package computer;
import java.util.HashMap;
public class Test {
public static void main(String[] args) {
WebsiteFactory f=new WebsiteFactory();
Website w1=f.getWebsite("产品展示 ");
w1.use(new User("用户1"));
Website w2=f.getWebsite("产品展示 ");
w2.use(new User("用户2"));
Website w3=f.getWebsite("产品展示 ");
w3.use(new User("用户3"));
Website w4=f.getWebsite("博客 ");
w4.use(new User("用户4"));
Website w5=f.getWebsite("博客 ");
w5.use(new User("用户5"));
Website w6=f.getWebsite("博客 ");
w6.use(new User("用户6"));
System.out.println("网站分类总数为:"+f.getWebsiteCount());
}
}
//外部状态,用户类
class User {
//用户名
private String name="";
public User(String name) {
this.name=name;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
//网站抽象类
abstract class Website{
public abstract void use(User user);
}
//具体网站类
class ConcreteWebsite extends Website{
//网站名
private String name="";
public ConcreteWebsite(String name) {
this.name=name;
}
public void use(User user) {
System.out.println("网站分类:"+name+"用户名:"+user.getName());
}
}
//网站工厂类
class WebsiteFactory{
private static HashMap<String,Website> pool=new HashMap<String,Website>();
public Website getWebsite(String webName) {
Website website=null;
if(pool.containsKey(webName)) {
website=pool.get(webName);
}else {
website=new ConcreteWebsite(webName);
pool.put(webName, website);
}
return website;
}
public int getWebsiteCount() {
return pool.size();
}
}
享元模式在什么情况下使用:
一个系统有大量的对象。
对象消耗大量的内存。
这些对象的状态中的大部分都可以外部化。
这些对象都可以按照内部状态分为很多组,当把外部对象从对象中剔除时,每一个组都可以仅用一个对象代替。
享元模式本质:分离与共享