享元模式
一、定义
GOF:运用共享技术有效地支持大量细粒度的对象。
二、引入
有些应用程序得益于在其整个设计过程中采用对象技术,但简单化的实现代价极大。使用面向对象的抽象化,可能会造成庞大的对象群,造成空间的巨大消耗,而影响性能。
在文档编辑器例子中如果一个字符对应一个对象,那么一篇文档所要容纳的对象将是非常的庞大耗费大量的内存。
而实际组成文档的字符是有限的,是由这些字符不同的组合和排列得到的。所以需要共享,将基本的字符进行共享,将使得字符对象变得有限。
也就是说在一个系统中如果有多个相同的对象,那么只共享一份就可以了,不必每个都去实例化一个对象。比如说一个文本系统,每个字母定一个对象,那么大小写字母一共就是52个,那么就要定义52个对象。
三、原理:
享元对象能做到共享的关键是区分内蕴状态(Internal State)和外蕴状态(ExternalState).
一个内蕴状态是存储在享元对象内部的,并且不会随环境改变而有所不同.因此,一个享元可以具有内蕴状态并可以共享.如字符代码……
一个外蕴状态是随环境改变而改变的,不可以共享状态.享元对象的外蕴状态必须由客户端保存,并在享元对象被创建之后,在需要使用的时候再传到享元对象内部.如字符位置,字符颜色……
外蕴状态不可以影响享元对象的内蕴状态,他们是相互独立的.所有的内蕴状态在对象创建完后就不可再改变.
四、结构
- 抽象享元角色(Flyweight):此角色是所有的具体享元类的超类,为这些类规定出需要实现的公共接口或抽象类。那些需要外蕴状态(External State)的操作可以通过方法的参数传入。抽象享元的接口使得享元变得可能,但是并不强制子类实行共享,因此并非所有的享元对象都是可以共享的。
- 具体享元(ConcreteFlyweight)角色:实现抽象享元角色所规定的接口。如果有内蕴状态的话,必须负责为内蕴状态提供存储空间。享元对象的内蕴状态必须与对象所处的周围环境无关,从而使得享元对象可以在系统内共享。有时候具体享元角色又叫做单纯具体享元角色,因为复合享元角色是由单纯具体享元角色通过复合而成的。
- 复合享元(UnsharableFlyweight)角色:复合享元角色所代表的对象是不可以共享的,但是一个复合享元对象可以分解成为多个本身是单纯享元对象的组合。复合享元角色又称做不可共享的享元对象。这个角色一般很少使用。
- 享元工厂(FlyweightFactoiy)角色:本角色负责创建和管理享元角色。在Flyweight模式中,由于要产生各种各样的对象,所以在Flyweight(享元)模式中常出现Factory模式。Flyweight的内部状态是用来共享的,Flyweight factory负责维护一个对象存储池(Flyweight Pool)来存放内部状态的对象。本角色必须保证享元对象可以被系统适当地共享。当一个客户端对象请求一个享元对象的时候,享元工厂角色需要检查系统中是否已经有一个符合要求的享元对象,如果已经有了,享元工厂角色就应当提供这个已有的享元对象;如果系统中没有一个适当的享元对象的话,享元工厂角色就应当创建一个新的合适的享元对象。 。
- 客户端(Client)角色:本角色还需要自行存储所有享元对象的外蕴状态。
五、example文本编辑器
(例子原地址 http://www.cnblogs.com/itTeacher/archive/2012/12/20/2826058.html)
编辑器内文字是享元对象中的内蕴状态,文字在文本中风格是外蕴状态.
风格类
public final class Style { private String font;//字体 private int Size;//字号 public Style(String font, int size) { this.font = font; Size = size; } public String getFont() { return font; } public void setFont(String font) { this.font = font; } public int getSize() { return Size; } public void setSize(int size) { Size = size; } }
Flyweight
public interface Flyweight { public void Operation(Style style); }
ConcreteFlyweight
public class ConcreteFlyweight implements Flyweight { private final String text; // internal state public ConcreteFlyweight(String text) { this.text = text; } // external state @Override public void Operation(Style style) { System.out.println(this.text + ". font is " + style.getFont() + " fontsize is " + style.getSize()); } }
CompositeFlyweight
import java.util.HashMap; import java.util.Iterator; import java.util.Map; public class CompositeFlyweight implements Flyweight { private final Map<String, Flyweight> texts = new HashMap<String, Flyweight>(); public void add(final String text, final Flyweight fly) { texts.put(text, fly); } @Override public void Operation(Style style) { for (final Iterator it = texts.entrySet().iterator(); it.hasNext();) { final Map.Entry textEntry = (Map.Entry) it.next(); final Flyweight fly = (Flyweight) textEntry.getValue(); fly.Operation(style); } } }
FlyweightFactory
import java.util.HashMap; import java.util.List; import java.util.Map; public class FlyweightFactory { private final static Map<String, Flyweight> texts = new HashMap<String, Flyweight>(); public Flyweight factory(final String text) { if (texts.containsKey(text)) { return texts.get(text); } else { final Flyweight fly = new ConcreteFlyweight(text); texts.put(text, fly); return fly; } } public CompositeFlyweight factory(final List<String> texts) { final CompositeFlyweight compositeFly = new CompositeFlyweight(); for (final String text : texts) { compositeFly.add(text, this.factory(text)); } return compositeFly; } public static void getSize() { System.out.println(texts.size()); } }
场景类1
public class Client { public static void main(final String[] args) { final FlyweightFactory factory = new FlyweightFactory(); //set internal state final String text1 = "this is intrnal state1"; final String text2 = "this is intrnal state2"; final String text3 = "this is intrnal state1"; final Flyweight fly1 = factory.factory(text1); final Flyweight fly2 = factory.factory(text2); final Flyweight fly3 = factory.factory(text3); //set external state final Style style = new Style("Sim", 10); fly1.Operation(style); fly2.Operation(style); fly3.Operation(style); FlyweightFactory.getSize(); } }
运行结果
this is intrnal state1. font is Sim fontsize is 10 this is intrnal state2. font is Sim fontsize is 10 this is intrnal state1. font is Sim fontsize is 10 共享的内部状态的个数为2 |
场景类2 带styleComposite
import java.util.ArrayList; import java.util.List; public class Client { public static void main(final String[] args) { final FlyweightFactory factory = new FlyweightFactory(); //set internal state final String text = "this is internal state"; final Flyweight fly = factory.factory(text); //set external state final Style style = new Style("Sim", 10); fly.Operation(style); //composite //set internal state final List<String> texts = new ArrayList<String>(); final String text1 = "this is internal state"; texts.add(text1); final String text2 = "this is internal state1"; texts.add(text2); final String text3 = "this is internal state2"; texts.add(text3); final Flyweight flyComposite = factory.factory(texts); //set external state final Style styleComposite = new Style("times newman", 11); flyComposite.Operation(styleComposite); FlyweightFactory.getSize(); } }
运行结果
this is internal state. font is Sim fontsize is 10 this is internal state1. font is times newman fontsize is 11 this is internal state. font is times newman fontsize is 11 this is internal state2. font is times newman fontsize is 11 共享的内部状态的个数为3 |
六、适用性
当以下情况都成立时使用Flyweight模式:
•一个应用程序使用了大量的对象。
•完全由于使用大量的对象,造成很大的存储开销。
•对象的大多数状态都可变为外部状态。
•如果删除对象的外部状态,那么可以用相对较少的共享对象取代很多组对象。
•应用程序不依赖于对象标识。由于Flyweight对象可以被共享,对于概念上明显有别的对象,标识测试将返回真值。
该模式的可用性在很大程度上取决于是否容易识别外部状态并将它从共享对象中删除
七、优缺点
7.1、优点
- 减少对象数量,节省内存空间。
- 享元模式使得系统更加复杂。为了使对象可以共享,需要将一些状态外部化,这使得程序的逻辑复杂化。
7.2、缺点
-
维护共享对象,需要额外的开销(用一个线程来维护垃圾回收)。
- 享元模式将享元对象的状态外部化,而读取外部状态使得运行时间稍微变长。
八、String中的享元模式
当我们在程序中直接用双引号括起来一个字符串时,JVM就到String的对象池里面去找看是否有一个值相同的对象,如果有,就拿现成的对象,如果没有就在对象池里面创建一个对象,并返回。
package flyweight; public class StringTest { public static void main(String[] args) { String str1 = "hello"; String str2 = "hello"; String str3 = new String("hello"); String str4 = "he" + "llo"; String sub1 = "he"; String sub2 = "llo"; String str5 = sub1 + "llo"; String str6 = "he" + sub2; String str7 = sub1 + sub2; System.out.println(str1 == str2);// true System.out.println(str1 == str3);// false System.out.println(str1 == str4);// true System.out.println(str1 == str5);// false System.out.println(str1 == str6);// false System.out.println(str1 == str7);// false } }
规则:
采用new 创建的字符串对象不进入字符串池
字符串相加的时候,都是静态字符串的结果会添加到字符串池,
如果其中含有变量(str5,str6,str7)则不会进入字符串池中。
九、example 打印准考证的例子
准考证上只有准考证号和姓名是唯一的,考场和考试科目基本上数目一定,可以共享。
外部状态(封装了)
public class Candidate { private int number; //考号 private String name; //考生姓名 public Candidate(int number, String name) { this.number = number; this.name = name; } public int getNumber() { return number; } public void setNumber(int number) { this.number = number; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
内蕴状态类 享元对象的内蕴状态必须与对象所处的周围环境无关,从而使得享元对象可以在系统内共享。
public class ExaminationInfo { protected String subject;//考试科目 protected String location;//考试地点 public ExaminationInfo(String subject, String location) { this.subject = subject; this.location = location; } public String getSubject() { return subject; } public void setSubject(String subject) { this.subject = subject; } public String getLocation() { return location; } public void setLocation(String location) { this.location = location; } }
Flyweight
public abstract class Flyweight { public abstract void printSignInfo(Candidate candidateInfo);//通过方法的参数传入外蕴状态 }
ConcreteFlyweight
public class ConcreteFlyweight extends Flyweight { private ExaminationInfo info; //内部对象 public ConcreteFlyweight(ExaminationInfo info) { this.info = info; } @Override public void printSignInfo(Candidate candidateInfo) { System.out.print("准考证信息 :"); System.out.print(" 准考证号: " + candidateInfo.getNumber()); System.out.print(" 考生姓名: " + candidateInfo.getName()); System.out.print(" 科目: " + info.getSubject()); System.out.print(" 地点: " + info.getLocation()); System.out.println(); } public ExaminationInfo getInfo() { return info; } public void setInfo(ExaminationInfo info) { this.info = info; } }
FlyweightFactory
import java.util.HashMap; import java.util.Map; public class FlyweightFactory { private static Map<ExaminationInfo, Flyweight> ExaminationInfos = new HashMap<ExaminationInfo, Flyweight>(); private FlyweightFactory() { } public static Flyweight createExaminationInfo(ExaminationInfo info) { Flyweight ExaminationState = ExaminationInfos.get(info); if (ExaminationState == null) { ExaminationState = new ConcreteFlyweight(info); ExaminationInfos.put(info, ExaminationState); } return ExaminationState; } public static int examinationInfosCount() { return ExaminationInfos.size(); } }
Client
public class Client { public static void main(String[] args) { ExaminationInfo info1 = new ExaminationInfo("英语六级", "第5考场"); ExaminationInfo info2 = new ExaminationInfo("英语四级", "第3考场"); Flyweight flyweight = FlyweightFactory.createExaminationInfo(info1); Candidate candidate1 = new Candidate(110, "张三"); flyweight.printSignInfo(candidate1); Flyweight flyweight2 = FlyweightFactory.createExaminationInfo(info2); Candidate candidate2 = new Candidate(112, "李四"); flyweight2.printSignInfo(candidate2); Flyweight flyweight3 = FlyweightFactory.createExaminationInfo(info1); Candidate candidate3 = new Candidate(119, "王五"); flyweight3.printSignInfo(candidate3); Flyweight flyweight4 = FlyweightFactory.createExaminationInfo(info2); Candidate candidate4 = new Candidate(120, "赵六"); flyweight4.printSignInfo(candidate4); System.out.println("内部状态(考试内容+考试地点)实例的个数为: "+ FlyweightFactory.examinationInfosCount()); } }
运行结果
准考证信息 : 准考证号: 110 考生姓名: 张三 科目: 英语六级 地点: 第5考场 准考证信息 : 准考证号: 112 考生姓名: 李四 科目: 英语四级 地点: 第3考场 准考证信息 : 准考证号: 119 考生姓名: 王五 科目: 英语六级 地点: 第5考场 准考证信息 : 准考证号: 120 考生姓名: 赵六 科目: 英语四级 地点: 第3考场 内部状态(考试内容+考试地点)实例的个数为: 2 |
参考
http://www.cnblogs.com/itTeacher/archive/2012/12/20/2826058.html
http://tzj163.blog.163.com/blog/static/10961167920107292287543/
http://www.cnblogs.com/zemliu/archive/2012/11/17/2775241.html