您的位置:首页 > 编程语言 > Java开发

Java 设计模式之享元模式的详解(结构模式)

2016-07-31 18:26 651 查看
享元模式的主要目的是实现对象的共享,即共享池,当系统中对象多的时候可以减少内存的开销,通常与工厂模式一起使用

享元模式的角色有:

抽象享元(Flyweight)角色:是给实现享元提供的接口。

具体享元(ConcreteFlyweight)角色:实现抽象角色,此对象必须是共享的,所含的状态必须是内部状态。

不共享享元(UnSharedConcreteFlyweight)角色:此对象不可共享,不是所有实现抽象享元接口的的对象都要共享,此对象通常将ConcreteFlyweight作为组成元素。

享元工厂(FlyweightFactory)角色:负责创建和管理享元角色,确保合理共享。

客户端(Client)角色:维持一个Flyweight对象的引用,计算或存储一个(多个)外部存储状态。

享元模式的类图如下:



享元模式在java.lang.String设计上的使用,我们知道java中字符串始终保持共享一份,如下面代码片段:

String m = "a";

String n = "a";

System.out.println(m==n);

这样会输出true,说明m和n指向了同一个实例,内存中也只有一个"a"。这就是享元模式在String上的使用。

享元模式在文字编辑存贮过程中的使用,这里假定文章由行对象组成,行对象由若干个字符对象组成,但是如果每个字符都保存自己的对象,那么一篇文章成千上万个字符对象,这样严重消耗系统内存,造成不可接受的运行时开销,好的方法是利用享元模式,只保存ASCII字符编码值,作为内部不变的状态,对当个字符对象进行共享,而相对字符颜色、大小这样的格式化数据作为外部状态,由客户端维护,运行时由外部传入即可。每个行作为不可共享享元对象,它是由享元对象(字符对象)组合而成的。



举例代码如下:

抽象享元角色:

public interface Glyph {

public abstract void draw(Context context);
}具体享元角色:
public class Character implements Glyph{

private char c;
private int size;
@Override
public void draw(Context context) {
// TODO Auto-generated method stub
this.size =context.getSize();
System.out.println(size+"号"+c+"被画出!");
}
public Character(char c) {
super();
this.c = c;
System.out.println(c+"被创建!");
}
public char getC() {
return c;
}
public void setC(char c) {
this.c = c;
}

}
不共享享元角色:
public class Row implements Glyph {

private List<Character> list = new ArrayList<>();

@Override
public void draw(Context context) {
// TODO Auto-generated method stub

}

public Row() {

}

public void setCharacter(Glyph r) {
list.add((Character) r);
}

public int getSize() {
return list.size();
}

public String getRow() {
StringBuilder sb = new StringBuilder();
for (Character g : list) {
sb.append(g.getC());
}
return sb.toString();
}

}享元工厂角色:
public class GlyphFactory {

private Map<String, Glyph> map = new HashMap<>();

public Glyph getGlyph(Context context) {
String cStr = context.getC() + "";
Glyph gl = map.get(cStr);
if (gl == null) {
gl = new Character(context.getC());
map.put(cStr, gl);
}
gl.draw(context);
return gl;
}
}数据类:
public class Context {

private int size;
private char c;

public int getSize() {
return size;
}

public void setSize(int size) {
this.size = size;
}

public char getC() {
return c;
}

public void setC(char c) {
this.c = c;
}

public Context(int size, char c) {
super();
this.size = size;
this.c = c;
}
}
客户端角色:
public class Client {

public static void main(String[] args) {
Row r = new Row();
GlyphFactory factory = new GlyphFactory();
Context context1 = new Context(12, 'a');
Glyph gly1 = factory.getGlyph(context1);
r.setCharacter(gly1);

Context context2 = new Context(13, 'a');
Glyph gly2 = factory.getGlyph(context2);
r.setCharacter(gly2);

Context context3 = new Context(13, 'b');
Glyph gly3 = factory.getGlyph(context3);
r.setCharacter(gly3);

System.out.println(r.getRow());
}
}
为了方便这届就写到main方法里面了,运行结果如图:



可以看出a创建了一次 ,a的大小12
,13是外部状态所以是外部传入,外部状态不能在享元内保存,而a字符是内部状态,行Row是有字符Character
组成,Row虽然实现了抽象享元接口,但是并没有再工厂中体现共享,因为他是不可共享的享元。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息