水不激不跃,人不激不奋
一,定义
使用共享对象可有效地支持大量的细粒度的对象
享元模式是对象池的一种实现,用来尽可能减少内存使用量,它适合用于可能存在大量重复对象的场景,来缓存可共享的对象,达到对象共享,避免创建过多对象的效果,这样一来就可以提升性能,避免内存溢出等。
在享元模式中会建立一个对象容器,在经典的享元模式中该容器为一个Map,它的键是享元对象的内部状态,它的值就是享元对象本身。客户端程序通过这个内部状态从享元工厂中获取享元对象,如果有缓存,则使用缓存对象,否则创建一个享元对象并且存入容器中,这样一来就避免了创建过多对象的问题。
二,使用场景
1,系统中存在大量的相似对象
2,细粒度的对象都具备较接近的外部状态,而且内部状态与环境无关,就是说对象没有特定身份
3,需要缓冲池的场景
三,角色介绍
Flyweight:享元对象抽象基类或接口
ConcreteFlyweight:具体的享元对象
FlyweightFactory:享元工厂,负责管理享元对象池和创建享元对象
四,使用案例
假设在吃鸡游戏中,每局游戏都会有100个玩家参与游戏,所以每局游戏都会创建100个玩家角色,如果一个玩家进行了10局游戏,他的角色就会被创建10次。这样就会造成频繁的GC,从而影响性能。如果我们使用享元模式,将玩家的姓名作为key,玩家角色作为value存放在内存中,这样就不会频繁的去创建玩家角色,避免造成不必要的资源浪费。
首先,创建一个玩家角色接口作为享元对象抽象接口,该接口定义玩家自己选择皮肤玩游戏:
public interface Player {
/**
* 玩游戏
* @param skin 自己选择的皮肤
* */
void playGame(String skin);
}
然后创建具体的享元对象 :
public class ConcretePlayer implements Player{
private String name;
public ConcretePlayer(String name) {
this.name = name;
}
@Override
public void playGame(String skin) {
System.out.println("玩家"+name+"使用"+skin+"角色玩游戏");
}
}
最后创建享元工厂:
public class PlayerFactory {
private static HashMap<String,Player> map =new HashMap<>();
public static Player createPlayer(String name){
if(map.get(name)==null){
System.out.println("创建新的角色");
ConcretePlayer concretePlayer =new ConcretePlayer(name);
map.put(name,concretePlayer);
return concretePlayer;
}else {
System.out.println("从缓存中拿取角色");
ConcretePlayer player = (ConcretePlayer) map.get(name);
return player;
}
}
}
使用:
Player player = PlayerFactory.createPlayer("N港之王");
player.playGame("红螳螂");
Player player1 = PlayerFactory.createPlayer("N港之王");
player1.playGame("黄螳螂");
Player player2 = PlayerFactory.createPlayer("N港之王");
player2.playGame("紫螳螂");
输出:
这样,即使玩家使用同一角色玩了好多把游戏,都只创建了一次角色,其余的都是从缓存中获取的
五,总结
享元模式实现比较简单,但是它的作用在某些场景确实及其重要。它可以大大减少应用程序创建的对象,降低程序内存的占用,增强程序的性能,但它同时也提高了系统的复杂性,需要分离出外部状态和内部状态,而且外部状态具有固化特性,不应该随内部状态改变而改变,否则导致系统的逻辑混乱。
享元模式的优点在于它大幅度地降低内存中对象的数量,但是,它做到这一点所付出的代价也是很高的。
享元模式使得系统更加复杂。为了使对象可以共享,需要将一些状态外部化,这使得程序的逻辑复杂化。
享元模式将享元对象的状态外部化,而读取外部状态使得运行时间稍微变长。
参考文献:Android源码设计模式解析与实战