携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第23天,点击查看活动详情
1.享元(Flyweight)模式概念:
面向对象的思想很好地解决了抽象性的问题,一般也不会出现性能上的问题。但是在某些情况下,对象的数量可能会太多,从而导致了运行时的代价。那么我们如何去避免大量细粒度的对象,同时又不影响客户程序面向对象的方式进行操作?
2.意图:
运行共享技术有效地支持大量细粒度的对象
3.享元模式的构成:
- 抽象享元角色(Flyweight):声明一个接口,通过它可以接收外来的参数(状态),并对新状态作出处理
- 共享具体享元类(Shared Concrete Flyweight):实现Flyweight接口,并为内部状态(如果有的话)增加存储空间,Shared Concrete Flyweight对象必须是可共享的,它所存储的状态必须是内部的,即它独立存在于自己的环境中
- 不共享具体享元类(Unshared Concrete Flyweight):不能共享的享元类,又叫做复合享元类。一个复合享元对象是由多个单享元对象组成,这些组成的对象是可以共享的,但是复合享元类本身并不能共享
- 轻量级类工厂(Flyweight Factory):
- 创建并管理Flyweight对象
- 确保享用Flyweight。当用户请求一个Flyweight时,Flyweight Factory提供一个已创建的示例或创建一个实例(如果不存在)
- 客户端(Client):
- 维持一个对Flyweight的引用
- 计算或存储一个或多个Flyweight的外部状态。
4.享元模式UML类图:
5.享元模式UML序列图:
6.处理过程:
客户初次从轻量级类工厂取Flyweight时,轻量级类工厂创建一个新的具体Flyweight对象,并保存起来,下次客户取用时,就无需再进行创建了,直接在保存池中返回。客户端负责处理Flyweight的状态。
7.享元模式:
- 享元的英文是Flyweight,它是一个来自体育方面的专业用语,在拳击、摔跤和举重比赛中特指最轻量级的级别。把这个单词移植到软件工程里面,也是用来表示特别小的对象,即细粒度对象。至于为什么我们把Flyweight翻译为“享元”,可以理解为共享元对象,也就是共享细粒度对象。享元模式就是通过使用共享的方式,达到高效地支持大量的细粒度对象。它的目的就是节省占用的空间资源,从而实现系统性能的改善。
- 我们把享元对象的所有状态分为两类,其实前面的例子中letter和fantasize属性在运行时,就形成了两类不同的状态。
- 享元对象的第一类状态称为内蕴状态(Internal State)。它不会随环境改变而改变,存储在享元对象内部,因此内蕴状态是可以共享的,对于任何一个享元对象来说,它的值是完全相同的。我们例子中Character类的letter属性,它代表的状态就是内蕴状态。
- 享元对象的第二类状态称为外蕴状态(External State)。它会随环境的改变而改变,因此是不可以共享的状态,对于不同的享元对象来讲,它的值可能是不同的。享元对象的外蕴状态必须有客户端保存,在享元对象被创建之后,需要使用的时候在传入到享元对象内部。我们例子中Character类的fantasize属性,它代表的状态就是外蕴状态。
- 享元的外蕴状态于内蕴状态是两类互相独立的状态,彼此没有关联。
8.适用性:
- 系统需要存在大量的对象而共享某些本质的,不变的信息。
- 对象可以同时用于多个环境下。
- 在每个实例下,Flyweight可以作为一个独立的对象。
9.代码示例:
抽象享元角色:
public abstract class Character {
protected char letter;
protected int fontSize;
public abstract void display();
public abstract void mySetFontSize(int fontSize);
protected int getFontSize() {
return fontSize;
}
protected void setFontSize(int fontSize) {
this.fontSize = fontSize;
}
}
共享具体享元角色A:
public class CharacterA extends Character {
public CharacterA() {
this.letter = 'A';
this.fontSize = 12;
}
@Override
public void display() {
System.out.println(this.letter + " " + this.fontSize);
}
@Override
public void mySetFontSize(int fontSize) {
this.fontSize = fontSize;
}
}
共享具体享元角色B:
public class CharacterB extends Character {
public CharacterB() {
this.letter = 'B';
this.fontSize = 12;
}
@Override
public void display() {
System.out.println(this.letter + " " + this.fontSize);
}
@Override
public void mySetFontSize(int fontSize) {
this.fontSize = fontSize;
}
}
轻量级类工厂:
public class CharacterFactory {
private Hashtable characters = new Hashtable();
private static CharacterFactory instance;
private CharacterFactory() {
characters.put("A", new CharacterA());
characters.put("B", new CharacterB());
}
// 一定要保证工厂是单例的
public static CharacterFactory getInstance() {
if (null == instance) {
instance = new CharacterFactory();
}
return instance;
}
// 此处才是享元模式的关键所在
public Character getCharacter(String key) {
Character character = (Character) characters.get(key);
if (null == character) {
switch (key) {
case "A":
character = new CharacterA();
break;
case "B":
character = new CharacterB();
break;
}
characters.put(key, character);
}
return character;
}
}
客户角色:
public class Client {
public static void main(String[] args) {
CharacterFactory factory = CharacterFactory.getInstance();
Character a = factory.getCharacter("A");
a.setFontSize(20);
a.display();
System.out.println("-------");
// Character a2 = factory.getCharacter("A");
// a2.display();
Character b = factory.getCharacter("B");
b.setFontSize(24);
b.display();
}
}