设计模式——享元模式

基本介绍

享元模式(Flyweight Pattern):也叫 蝇量模式: 运用共享技术有效地支持大量细粒度的对象

类图

1) FlyWeight 是抽象的享元角色, 他是产品的抽象类, 同时定义出对象的外部状态和内部状态(后面介绍) 的接口或实现
2) ConcreteFlyWeight 是具体的享元角色,是具体的产品类,实现抽象角色定义相关业务 
3) UnSharedConcreteFlyWeight 是不可共享的角色,一般不会出现在享元工厂。
4) FlyWeightFactory 享元工厂类,用于构建一个池容器(集合),同时提供从池中获取对象方法

在这里插入图片描述

内部状态和外部状态

内部状态指对象共享出来的信息,存储在享元对象内部且不会随环境的改变而改变 
外部状态指对象得以依赖的一个标记,是随环境改变而改变的、不可共享的状态

场景一:共享网站

类图

在这里插入图片描述

UnsharedConcreteWebSite在此处并未体现出其用途,本文仅将其代码展现出来

代码

WebSite

public abstract class WebSite {
    public abstract void use(User user);
}

User

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;
    }
}

ConcreteWebSite

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());
    }
}

UnSharedConcreteWebSite

public class UnSharedConcreteWebSite extends WebSite{
    @Override
    public void use(User user) {
        System.out.println("这是不共享的网站");
    }
}

WebSiteFactory

public class WebSiteFactory {
    private Hashtable<String,WebSite> webSites=new Hashtable<>();
    public WebSite getFlyweight(String name){
        if(!webSites.contains(name)){
            webSites.put(name,new ConcreteWebSite(name));
        }
        return webSites.get(name);
    }
    public int getWebSiteCount(){
        return webSites.size();
    }
}

Main

public class Main {
    public static void main(String[] args) {
        WebSiteFactory webSiteFactory = new WebSiteFactory();
        WebSite webSite01 = webSiteFactory.getFlyweight("新闻");
        webSite01.use(new User("张三"));
        webSite01.use(new User("李四"));
        webSite01.use(new User("王五"));
        WebSite webSite02 = webSiteFactory.getFlyweight("博客");
        webSite02.use(new User("赵六"));
        System.out.println("网站的数量:"+webSiteFactory.getWebSiteCount());
    }
}

结果

网站类别:新闻,使用者:张三
网站类别:新闻,使用者:李四
网站类别:新闻,使用者:王五
网站类别:博客,使用者:赵六
网站的数量:2

场景二:JDK-Interger 的应用源码分析

测试

public static void main(String[] args) {
        // TODOAuto-generated method stub
        // 如果 Integer.valueOf(x) x 在 -128 --- 127 直接,就是使用享元模式返回,如果不在
        // 范围类,则仍然 new
        //小结:
        // 1. 在 valueOf 方法中,先判断值是否在 IntegerCache 中,如果不在,就创建新的 Integer(new), 否则,就直接从缓存池返回
        // 2. valueOf 方法,就使用到享元模式 //3. 如果使用 valueOf 方法得到一个 Integer 实例,范围在 -128 - 127 ,执行速度比 new 快
        Integer x = Integer.valueOf(127); // 得到 x 实例,类型 Integer
        Integer y = new Integer(127); // 得到 y 实例,类型 Integer
        Integer z = Integer.valueOf(127);//..
        Integer w = new Integer(127);
        System.out.println(x.equals(y)); // 大小,true
        System.out.println(x == y); // false
        System.out.println(x == z); // true
        System.out.println(w == x); // false
        System.out.println(w == y); // false
    }

源码

public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }

场景三:数据库连接池

代码

class Pool { 
    // 1. 连接池大小 
    private final int poolSize;
	// 2. 连接对象数组 
    private Connection[] connections;
	// 3. 连接状态数组 0 表示空闲, 1 表示繁忙 
    private AtomicIntegerArray states;
	// 4. 构造方法初始化 
    public Pool(int poolSize) { 
        this.poolSize = poolSize; 
        this.connections = new Connection[poolSize];
        this.states = new AtomicIntegerArray(new int[poolSize]); 
        for (int i = 0; i < poolSize; i++) { 
            connections[i] = new MockConnection("连接" + (i+1));
		} 
    }
	// 5. 借连接
	public Connection borrow() {
        while(true) { 
            for (int i = 0; i < poolSize; i++) { 
                // 获取空闲连接 
                if(states.get(i) == 0) { 
                    if (states.compareAndSet(i, 0, 1)) { 
                        log.debug("borrow {}", connections[i]); 
                        return connections[i];
					} 
                } 
            }
			// 如果没有空闲连接,当前线程进入等待 
            synchronized (this) { 
                try {
				log.debug("wait..."); 
                    this.wait();
				} catch (InterruptedException e) { 
                    e.printStackTrace();
				} 
            }
        } 
    }
	// 6. 归还连接 
    public void free(Connection conn) { 
        for (int i = 0; i < poolSize; i++) { 
            if (connections[i] == conn) { 
                states.set(i, 0); 
                synchronized (this) { 
                    log.debug("free {}", conn); 
                    this.notifyAll();
				} 
                break; 
            } 
        }
    }
}
class MockConnection implements Connection { // 实现略
}

测试

Pool pool = new Pool(2); 
for (int i = 0; i < 5; i++) { 
    new Thread(() -> { 
        Connection conn = pool.borrow(); 
        try {
			Thread.sleep(new Random().nextInt(1000));
		} catch (InterruptedException e) { 
            e.printStackTrace();
		} 
        pool.free(conn); 
    }).start();
}

场景四:String常量池

代码

String a="hello";
String b="hello";
System.out.println(a==b);//true
String a="hello";
String b=new String("hello");
System.out.println(a==b);//false
String a=new String("hello");
String b="hello";
System.out.println(a==b);//false
String a=new String("hello");
String b=new String("hello");
System.out.println(a==b);//false

总结

①如果一个应用程序使用了大量的对象,而大量的这些对象造成了很大的存储开销时就应该考虑使用;
②对象的大多数状态可以是外部状态,如果删除了对象的外部状态,那么可以用相对较少的共享对象取代很多组对象,此时可以考虑享元模式

em.out.println(a==b);//false


```java
String a=new String("hello");
String b=new String("hello");
System.out.println(a==b);//false

总结

①如果一个应用程序使用了大量的对象,而大量的这些对象造成了很大的存储开销时就应该考虑使用;
②对象的大多数状态可以是外部状态,如果删除了对象的外部状态,那么可以用相对较少的共享对象取代很多组对象,此时可以考虑享元模式
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值