Appearance
享元模式
定义
享元模式(Flyweight Pattern)是一种结构型设计模式,它通过共享对象来减少内存使用和对象创建的开销。享元模式的核心思想是将对象的内部状态(不变的部分)与外部状态(可变的部分)分离,通过共享内部状态来减少对象的创建。
享元模式的主要角色包括:
- 享元工厂(Flyweight Factory):负责创建和管理享元对象,提供获取享元对象的方法。
- 享元对象(Flyweight):封装了对象的内部状态,提供对外部状态的访问方法。享元对象通常是一个抽象类或接口。
- 具体享元对象(Concrete Flyweight):实现了享元对象接口,封装了具体的内部状态。
- 客户端(Client):使用享元对象,通过享元工厂获取享元对象,并设置外部状态。
优点
- 减少内存使用:通过共享对象来减少内存使用,避免创建大量重复的对象。
- 提高性能:减少对象的创建和销毁的开销,提高程序的性能。
- 简化管理:通过享元工厂来管理享元对象,简化了对象的创建和管理。
缺点
- 增加复杂性:享元模式将对象的内部状态与外部状态分离,增加了系统的复杂性。需要仔细设计内部状态和外部状态,确保正确地共享对象。
- 可能不适用于所有场景:享元模式适用于对象数量较多且对象内部状态不变的场景。如果对象数量较少或对象内部状态经常变化,使用享元模式可能得不偿失。
- 可能增加维护成本:享元模式需要仔细管理享元对象的生命周期,确保正确地共享和回收对象。如果对象管理不当,可能导致内存泄漏或其他问题。
使用场景
- 对象数量较多且对象内部状态不变:当系统中存在大量重复的对象,且这些对象的内部状态不变时,可以使用享元模式来减少内存使用和提高性能。
- 需要频繁创建和销毁对象:当需要频繁创建和销毁对象时,可以使用享元模式来减少对象的创建和销毁的开销。
- 需要简化对象管理:当需要简化对象的管理时,可以使用享元模式来通过享元工厂来管理享元对象。
代码示例
这里创建一个游戏练功房,怪物随机生成,每个怪物的属性都是一样的,只有名字和经验不一样,所以可以使用享元模式来减少内存使用。
享元对象
java
public class Monster {
private static final Random random = new Random();
private String name;
private int exp;
private boolean alive;
public Monster(String name) {
this.name = name;
this.exp = random.nextInt(100) + 1;
this.alive = true;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getExp() {
return exp;
}
public void setExp(int exp) {
this.exp = exp;
}
public boolean isAlive() {
return alive;
}
public void setAlive(boolean alive) {
this.alive = alive;
}
}
享元工厂
java
public class TrainingRoom {
private static final Random RANDOM = new Random();
private static final Map<String, List<Monster>> MONSTER_MAP = new HashMap<>();
private static final List<Monster> currentMonsters = new ArrayList<>();
public static final String[] names = {
"哥布林", "野鸡", "野兔", "树精", "大黄蜂", "吸血蚊"
};
public static synchronized void create(String name){
List<Monster> monsters = MONSTER_MAP.computeIfAbsent(name, k -> new ArrayList<>());
Monster monster = monsters.isEmpty() ? null : monsters.get(RANDOM.nextInt(monsters.size()));
if (monster == null || monster.isAlive()) {
monster = new Monster(name);
} else if (!currentMonsters.contains(monster)){
monster.setAlive(true);
monster.setExp(RANDOM.nextInt(100) + 1);
}
currentMonsters.add(monster);
}
public static synchronized int defeatMonster() {
if (currentMonsters.isEmpty()) {
System.out.println("怪物生成中...");
return 0;
}
int i = RANDOM.nextInt(currentMonsters.size());
Monster monster = currentMonsters.get(i);
currentMonsters.remove(monster);
monster.setAlive(false);
System.out.print(String.format("击败怪物[%-4s],经验+%d\n",
monster.getName(), monster.getExp())
.replaceAll(" ", "\u3000"));
return monster.getExp();
}
public static synchronized boolean isEmpty() {
return currentMonsters.isEmpty();
}
}
客户端
练功十秒钟
java
public class Client {
public static void main(String[] args) {
final Random random = new Random();
AtomicInteger totalExp = new AtomicInteger(0);
// 练功房练功10秒
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);
ScheduledFuture<?> future = executorService.scheduleAtFixedRate(() -> {
int size = random.nextInt(20) + 10;
for (int i = 0; i < size; i++) {
TrainingRoom.create(TrainingRoom.names[random.nextInt(TrainingRoom.names.length)]);
}
while (!TrainingRoom.isEmpty()) {
totalExp.addAndGet(TrainingRoom.defeatMonster());
}
}, 0, 180, TimeUnit.MILLISECONDS);
// 10秒后结束
executorService.schedule(() -> {
future.cancel(false);
System.out.printf("练功结束,经验: %d\n", totalExp.get());
}, 10, TimeUnit.SECONDS);
// 10秒后中止线程池
executorService.schedule(executorService::shutdown, 15, TimeUnit.SECONDS);
try {
Thread.sleep(20000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
运行结果
txt
...
...
击败怪物[野兔 ],经验+62
击败怪物[大黄蜂 ],经验+92
击败怪物[野鸡 ],经验+31
击败怪物[树精 ],经验+2
击败怪物[吸血蚊 ],经验+5
击败怪物[吸血蚊 ],经验+38
击败怪物[大黄蜂 ],经验+67
击败怪物[树精 ],经验+81
击败怪物[哥布林 ],经验+44
击败怪物[树精 ],经验+32
击败怪物[树精 ],经验+85
击败怪物[大黄蜂 ],经验+86
击败怪物[哥布林 ],经验+22
击败怪物[树精 ],经验+58
击败怪物[吸血蚊 ],经验+20
击败怪物[吸血蚊 ],经验+56
击败怪物[树精 ],经验+53
击败怪物[大黄蜂 ],经验+55
击败怪物[哥布林 ],经验+28
击败怪物[野鸡 ],经验+90
击败怪物[吸血蚊 ],经验+71
击败怪物[野兔 ],经验+35
击败怪物[野兔 ],经验+52
击败怪物[大黄蜂 ],经验+12
击败怪物[吸血蚊 ],经验+5
击败怪物[野兔 ],经验+54
击败怪物[哥布林 ],经验+94
击败怪物[吸血蚊 ],经验+28
击败怪物[吸血蚊 ],经验+11
击败怪物[哥布林 ],经验+28
击败怪物[野兔 ],经验+74
击败怪物[大黄蜂 ],经验+64
击败怪物[树精 ],经验+81
击败怪物[野鸡 ],经验+1
击败怪物[树精 ],经验+80
击败怪物[野兔 ],经验+35
击败怪物[哥布林 ],经验+79
击败怪物[大黄蜂 ],经验+76
击败怪物[树精 ],经验+16
击败怪物[吸血蚊 ],经验+19
击败怪物[大黄蜂 ],经验+64
击败怪物[野鸡 ],经验+92
击败怪物[野鸡 ],经验+70
击败怪物[野兔 ],经验+7
击败怪物[大黄蜂 ],经验+1
击败怪物[大黄蜂 ],经验+4
击败怪物[野兔 ],经验+46
击败怪物[哥布林 ],经验+99
击败怪物[大黄蜂 ],经验+42
击败怪物[哥布林 ],经验+94
击败怪物[野鸡 ],经验+44
击败怪物[野鸡 ],经验+48
击败怪物[哥布林 ],经验+86
击败怪物[野鸡 ],经验+38
击败怪物[大黄蜂 ],经验+43
击败怪物[树精 ],经验+40
击败怪物[哥布林 ],经验+20
击败怪物[野鸡 ],经验+85
击败怪物[哥布林 ],经验+34
击败怪物[哥布林 ],经验+86
击败怪物[哥布林 ],经验+76
击败怪物[野兔 ],经验+78
击败怪物[野鸡 ],经验+41
击败怪物[大黄蜂 ],经验+12
击败怪物[哥布林 ],经验+29
击败怪物[哥布林 ],经验+56
击败怪物[树精 ],经验+78
击败怪物[野兔 ],经验+29
击败怪物[吸血蚊 ],经验+17
击败怪物[树精 ],经验+22
击败怪物[哥布林 ],经验+50
击败怪物[大黄蜂 ],经验+50
击败怪物[哥布林 ],经验+8
击败怪物[吸血蚊 ],经验+66
击败怪物[大黄蜂 ],经验+58
击败怪物[哥布林 ],经验+30
击败怪物[吸血蚊 ],经验+67
击败怪物[吸血蚊 ],经验+80
击败怪物[野鸡 ],经验+29
击败怪物[大黄蜂 ],经验+49
击败怪物[树精 ],经验+6
击败怪物[哥布林 ],经验+96
击败怪物[吸血蚊 ],经验+76
击败怪物[野鸡 ],经验+90
击败怪物[树精 ],经验+28
击败怪物[树精 ],经验+34
击败怪物[野兔 ],经验+82
击败怪物[大黄蜂 ],经验+46
击败怪物[野兔 ],经验+16
击败怪物[树精 ],经验+5
击败怪物[吸血蚊 ],经验+76
击败怪物[大黄蜂 ],经验+31
击败怪物[野鸡 ],经验+7
击败怪物[大黄蜂 ],经验+52
击败怪物[树精 ],经验+92
击败怪物[树精 ],经验+33
击败怪物[野鸡 ],经验+69
击败怪物[大黄蜂 ],经验+6
击败怪物[野鸡 ],经验+83
击败怪物[哥布林 ],经验+58
击败怪物[吸血蚊 ],经验+27
击败怪物[哥布林 ],经验+71
击败怪物[野兔 ],经验+13
击败怪物[大黄蜂 ],经验+100
击败怪物[哥布林 ],经验+82
击败怪物[树精 ],经验+89
击败怪物[野鸡 ],经验+63
击败怪物[大黄蜂 ],经验+35
击败怪物[野鸡 ],经验+34
击败怪物[树精 ],经验+12
击败怪物[野鸡 ],经验+55
击败怪物[树精 ],经验+87
击败怪物[野兔 ],经验+39
击败怪物[吸血蚊 ],经验+45
击败怪物[吸血蚊 ],经验+8
击败怪物[树精 ],经验+51
击败怪物[吸血蚊 ],经验+20
练功结束,经验: 56265