享元模式

  定义:共享元对象,运用共享技术有效地支持大量细粒度对象的复用。如果在一个系统中存在多个相同的对象,那么只需要共享一份对象的拷贝,而不必为每一次使用创建新的对象。

享元模式是为数不多的、只为提升系统性能而生的设计模式,主要作用就是复用大对象(重量级对象),以节省内存空间和对象创建时间。

  面向对象可以非常方便的解决一些扩展性的问题,但是在这个过程中系统务必会产生一些类或者对象,如果系统中存在对象的个数过多时,将会导致系统的性能下降。对于这样的问题解决最简单直接的办法就是减少系统中对象的个数。享元模式提供了一种解决方案,使用共享技术实现相同或者相似对象的重用。也就是说实现相同或者相似对象的代码共享。

所谓享元模式就是运行共享技术有效地支持大量细粒度对象的复用。系统使用少量对象,而且这些都比较相似,状态变化小,可以实现对象的多次复用。

共享模式是支持大量细粒度对象的复用,所以享元模式要求能够共享的对象必须是细粒度对象。

首先了解两个概念:内部状态、外部状态。

内部状态:在享元对象内部不随外界环境改变而改变的共享部分。

外部状态:随着环境的改变而改变,不能够共享的状态就是外部状态。

由于享元模式区分了内部状态和外部状态,所以我们可以通过设置不同的外部状态使得相同的对象可以具备一些不同的特性,而内部状态设置为相同部分。在我们的程序设计过程中,我们可能会需要大量的细粒度对象来表示对象,如果这些对象除了几个参数不同外其他部分都相同,这个时候我们就可以利用享元模式来大大减少应用程序当中的对象。如何利用享元模式呢?这里我们只需要将他们少部分的不同的部分当做参数移动到类实例的外部,然后在方法调用的时候将他们传递过来就可以了。这里也就说明了一点:内部状态存储于享元对象内部,而外部状态则应该由客户端来考虑。

二、 模式结构

下图是享元模式的UML结构图

享元模式存在如下几个角色:

模式结构

  1. Flyweight: 享元接口,所有具体享元类的超类或接口,通过该接口Flyweight可以接受并作用于外部状态。通过该接口可以传入外部的状态,在享元对象的方法处理中可能会使用这些外部的数据。

  2. ConcreteFlyweight: 具体的享元实现对象,指定内部状态,必须是共享的,需要封装Flyweight的内部状态。

  3. UnshareConcreteFlyweight: 非共享的享元实现对象,并不是所有的Flyweight实现对象都需要共享。非共享的享元实现对象通常是对享元对象的组合对象。

  4. FlyweightFactoty: 享元工厂类,主要用来创建并管理共享的享元对象,并对外提供访问共享享元的接口。当用户请求一个Flyweight时,FlyweightFactory就会提供一个已经创建的Flyweight对象或者新建一个(如果不存在)。

  5. Client: 享元客户端,主要的工作就是维持一个对Flyweight的引用,计算或存储享元的外部状态,当然这里可访问共享和不共享的Flyweight对象。

享元模式的核心在于享元工厂类,享元工厂类的作用在于提供一个用于存储享元对象的享元池,用户需要对象时,首先从享元池中获取,如果享元池中不存在,则创建一个新的享元对象返回给用户,并在享元池中保存该新增对象。

三、 模式实现

1、单纯享元模式:在单纯享元模式中,所有的享元对象都是可以共享的,即所有抽象享元类的子类都可共享,不存在非共享具体享元类。

  

 //抽象享元角色类
public interface Flyweight {
//一个示意性方法,参数state是外蕴状态
public void operation(String state);
} //具体享元角色类
//具体享元角色类ConcreteFlyweight有一个内蕴状态,在本例中一个Character类型的intrinsicState属性代表,它的值应当在享元对象
//被创建时赋予。所有的内蕴状态在对象创建之后,就不会再改变了。如果一个享元对象有外蕴状态的话,所有的外部状态都必须存储在客户端,
//在使用享元对象时,再由客户端传入享元对象。这里只有一个外蕴状态,operation()方法的参数state就是由外部传入的外蕴状态。
public class ConcreteFlyweight implements Flyweight {
private Character intrinsicState = null;
/**
* 构造函数,内蕴状态作为参数传入
* @param state
*/
public ConcreteFlyweight(Character state){
this.intrinsicState = state;
} /**
* 外蕴状态作为参数传入方法中,改变方法的行为,
* 但是并不改变对象的内蕴状态。
*/
@Override
public void operation(String state) {
// TODO Auto-generated method stub
System.out.println("Intrinsic State = " + this.intrinsicState);
System.out.println("Extrinsic State = " + state);
} } //享元工厂角色类
//享元工厂角色类,必须指出的是,客户端不可以直接将具体享元类实例化,而必须通过一个工厂对象,利用一个factory()方法得到享元对象。
//一般而言,享元工厂对象在整个系统中只有一个,因此也可以使用单例模式。 //当客户端需要单纯享元对象的时候,需要调用享元工厂的factory()方法,并传入所需的单纯享元对象的内蕴状态,由工厂方法产生所需要的
//享元对象。
public class FlyweightFactory {
private Map<Character,Flyweight> files = new HashMap<Character,Flyweight>(); public Flyweight factory(Character state){
//先从缓存中查找对象
Flyweight fly = files.get(state);
if(fly == null){
//如果对象不存在则创建一个新的Flyweight对象
fly = new ConcreteFlyweight(state);
//把这个新的Flyweight对象添加到缓存中
files.put(state, fly);
}
return fly;
}
} //客户端类
public class Client { public static void main(String[] args) {
// TODO Auto-generated method stub
FlyweightFactory factory = new FlyweightFactory();
Flyweight fly = factory.factory(new Character('a'));
fly.operation("First Call"); fly = factory.factory(new Character('b'));
fly.operation("Second Call"); fly = factory.factory(new Character('a'));
fly.operation("Third Call");
} }

  虽然客户端申请了三个享元对象,但是实际创建的享元对象只有两个,这就是共享的含义。运行结果如下:

  

2、复合享元模式:将一些单纯享元使用组合模式加以组合,可以形成复合享元对象,这样的复合享元对象本身不能共享,但是它们可以分解成单纯享元对象,而后者则可以共享。                              

实现示例:

 //抽象享元角色类
public interface Flyweight {
//一个示意性方法,参数state是外蕴状态
public void operation(String state);
} //具体享元角色类
//具体享元角色类ConcreteFlyweight有一个内蕴状态,在本例中一个Character类型的intrinsicState属性代表,它的值应当在享元对象
//被创建时赋予。所有的内蕴状态在对象创建之后,就不会再改变了。如果一个享元对象有外蕴状态的话,所有的外部状态都必须存储在客户端,
//在使用享元对象时,再由客户端传入享元对象。这里只有一个外蕴状态,operation()方法的参数state就是由外部传入的外蕴状态。
public class ConcreteFlyweight implements Flyweight {
private Character intrinsicState = null;
/**
* 构造函数,内蕴状态作为参数传入
* @param state
*/
public ConcreteFlyweight(Character state){
this.intrinsicState = state;
} /**
* 外蕴状态作为参数传入方法中,改变方法的行为,
* 但是并不改变对象的内蕴状态。
*/
@Override
public void operation(String state) {
// TODO Auto-generated method stub
System.out.println("Intrinsic State = " + this.intrinsicState);
System.out.println("Extrinsic State = " + state);
} } //复合享元角色类
//复合享元对象是由单纯享元对象通过复合而成的,因此它提供了add()这样的聚集管理方法。由于一个复合享元对象具有不同的聚集元素,
//这些聚集元素在复合享元对象被创建之后加入,这本身就意味着复合享元对象的状态是会改变的,因此复合享元对象是不能共享的。
//复合享元角色实现了抽象享元角色所规定的接口,也就是operation()方法,这个方法有一个参数,代表复合享元对象的外蕴状态。
//一个复合享元对象的所有单纯享元对象元素的外蕴状态都是与复合享元对象的外蕴状态相等的;
//而一个复合享元对象所含有的单纯享元对象的内蕴状态一般是不相等的,不然就没有使用价值了。
public class ConcreteCompositeFlyweight implements Flyweight { private Map<Character,Flyweight> files = new HashMap<Character,Flyweight>();
/**
* 增加一个新的单纯享元对象到聚集中
*/
public void add(Character key , Flyweight fly){
files.put(key,fly);
}
/**
* 外蕴状态作为参数传入到方法中
*/
@Override
public void operation(String state) {
Flyweight fly = null;
for(Object o : files.keySet()){
fly = files.get(o);
fly.operation(state);
} } } //享元工厂角色类
//享元工厂角色提供两种不同的方法,一种用于提供单纯享元对象,另一种用于提供复合享元对象。
public class FlyweightFactory {
private Map<Character,Flyweight> files = new HashMap<Character,Flyweight>();
/**
* 复合享元工厂方法
*/
public Flyweight factory(List<Character> compositeState){
ConcreteCompositeFlyweight compositeFly = new ConcreteCompositeFlyweight(); for(Character state : compositeState){
compositeFly.add(state,this.factory(state));
} return compositeFly;
}
/**
* 单纯享元工厂方法
*/
public Flyweight factory(Character state){
//先从缓存中查找对象
Flyweight fly = files.get(state);
if(fly == null){
//如果对象不存在则创建一个新的Flyweight对象
fly = new ConcreteFlyweight(state);
//把这个新的Flyweight对象添加到缓存中
files.put(state, fly);
}
return fly;
}
} //客户端类
public class Client { public static void main(String[] args) {
List<Character> compositeState = new ArrayList<Character>();
compositeState.add('a');
compositeState.add('b');
compositeState.add('c');
compositeState.add('a');
compositeState.add('b'); FlyweightFactory flyFactory = new FlyweightFactory();
Flyweight compositeFly1 = flyFactory.factory(compositeState);
Flyweight compositeFly2 = flyFactory.factory(compositeState);
compositeFly1.operation("Composite Call"); System.out.println("---------------------------------");
System.out.println("复合享元模式是否可以共享对象:" + (compositeFly1 == compositeFly2)); Character state = 'a';
Flyweight fly1 = flyFactory.factory(state);
Flyweight fly2 = flyFactory.factory(state);
System.out.println("单纯享元模式是否可以共享对象:" + (fly1 == fly2));
}
}

  运行结果如下:

   

四、 模式优缺点

享元模式是一个 考虑系统性能的设计模式,通过使用享元模式可以节约内存空间,提高系统的性能。

优点

1、享元模式的优点在于它能够极大的减少系统中对象的个数。

2、享元模式由于使用了外部状态,外部状态相对独立,不会影响到内部状态,所以享元模式使得享元对象能够在不同的环境被共享。

缺点

1、由于享元模式需要区分外部状态和内部状态,使得应用程序在某种程度上来说更加复杂化了。

2、为了使对象可以共享,享元模式需要将享元对象的状态外部化,而读取外部状态使得运行时间变长。

五、 模式适用场景

JDK 里的享元模式:在 JDK 的设计里,也有很享元模式,如一些常量池的设计(String 常量池、Integer 常量池等等);

1、如果一个系统中存在大量的相同或者相似的对象,由于这类对象的大量使用,会造成系统内存的耗费,可以使用享元模式来减少系统中对象的数量。

2、对象的大部分状态都可以外部化,可以将这些外部状态传入对象中。

六、 模式总结

1、享元模式可以极大地减少系统中对象的数量。但是它可能会引起系统的逻辑更加复杂化。

2、享元模式的核心在于享元工厂,它主要用来确保合理地共享享元对象。

3、内部状态为不变共享部分,存储于享元享元对象内部,而外部状态是可变部分,它应当油客户端来负责。

设计模式12---享元模式(Flyweight Pattern)的更多相关文章

  1. 乐在其中设计模式(C#) - 享元模式(Flyweight Pattern)

    原文:乐在其中设计模式(C#) - 享元模式(Flyweight Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 享元模式(Flyweight Pattern) 作者:weba ...

  2. 设计模式-11享元模式(Flyweight Pattern)

    1.模式动机 在面向对象程序设计过程中,有时会面临要创建大量相同或相似对象实例的问题.创建那么多的对象将会耗费很多的系统资源,它是系统性能提高的一个瓶颈. 享元模式就是把相同或相似对象的公共部分提取出 ...

  3. 二十四种设计模式:享元模式(Flyweight Pattern)

    享元模式(Flyweight Pattern) 介绍运用共享技术有效地支持大量细粒度的对象. 示例有一个Message实体类,某些对象对它的操作有Insert()和Get()方法,现在要运用共享技术支 ...

  4. 【UE4 设计模式】享元模式 Flyweight Pattern

    概述 描述 运用共享技术有效地支持大量细粒度对象的复用.系统只使用少量的对象,而这些对象都很相似,状态变化很小,可以实现对象的多次复用. 由于享元模式要求能够共享的对象必须是细粒度对象,因此它又称为轻 ...

  5. 12.享元模式(Flyweight Pattern)

    using System; using System.Collections; namespace ConsoleApplication5 { class Program { /// <summ ...

  6. 设计模式系列之享元模式(Flyweight Pattern)——实现对象的复用

    说明:设计模式系列文章是读刘伟所著<设计模式的艺术之道(软件开发人员内功修炼之道)>一书的阅读笔记.个人感觉这本书讲的不错,有兴趣推荐读一读.详细内容也可以看看此书作者的博客https:/ ...

  7. Java享元模式(Flyweight Pattern)

    享元模式(Flyweight Pattern)主要用于减少创建的对象数量,并减少内存占用并提高性能. 这种类型的设计模式属于结构模式,因为该模式提供了减少对象计数的方法,从而改善应用的对象结构. 享元 ...

  8. C#设计模式(12)——享元模式(Flyweight Pattern)

    一.引言 在软件开发过程,如果我们需要重复使用某个对象的时候,如果我们重复地使用new创建这个对象的话,这样我们在内存就需要多次地去申请内存空间了,这样可能会出现内存使用越来越多的情况,这样的问题是非 ...

  9. C#设计模式(12)——享元模式(Flyweight Pattern)(转)

    一.引言 在软件开发过程,如果我们需要重复使用某个对象的时候,如果我们重复地使用new创建这个对象的话,这样我们在内存就需要多次地去申请内存空间了,这样可能会出现内存使用越来越多的情况,这样的问题是非 ...

  10. 设计模式之享元模式(Flyweight)摘录

    23种GOF设计模式一般分为三大类:创建型模式.结构型模式.行为模式. 创建型模式抽象了实例化过程,它们帮助一个系统独立于怎样创建.组合和表示它的那些对象.一个类创建型模式使用继承改变被实例化的类,而 ...

随机推荐

  1. 第六章 声明式服务调用: Spring Cloud Feign

    我们在使用 Spring Cloud Ribbon 时, 通常都会利用它对 RestTemplate 的请求拦截来实现对依赖服务的接口调用, 而 RestTemplate 已经实现了对 HTTP 请求 ...

  2. update project maven项目的时候出错

    preference node "org.eclipse.wst.validation"has been remove 上面的错误是因为修改包名无法互相引入导致的,仅仅需要将Ecl ...

  3. C#开发之反射的简单使用

    奋斗的蘑菇 原文C#开发之反射的简单使用 以前在Windows Mobile中写过一个写好的Dll中的图片的例子,现在在项目中有接触到在一个大的窗体中,动态的加载一些窗体这样的需求.将功能按照模块的划 ...

  4. Mysql之数据库设计

    一.三大范式 1.第一范式:消除一个字段包含多个数据库值,消除一个记录包含重复的组(单独的一列包含多个项目),即可满足1NF. 2.第二范式:消除部分依赖性即可转化为2NF.部分依赖性表示一个记录中包 ...

  5. Python之with语句原理

    我们看一个with处理文件操作的实例: with open('/etc/passwd') as f: for line in f: print(line) 这段代码的作用:打开一个文件,如果一切正常, ...

  6. SpringData JPA示例

    SpringData JPA只是SpringData中的一个子模块 JPA是一套标准接口,而Hibernate是JPA的实现 SpringData JPA 底层默认实现是使用Hibernate 1.  ...

  7. 【原】Coursera—Andrew Ng机器学习—Week 5 习题—Neural Networks learning

    课上习题 [1]代价函数 [2]代价函数计算 [3] [4]矩阵的向量化 [5]梯度校验 Answer:(1.013 -0.993) / 0.02 = 3.001 [6]梯度校验 Answer:学习的 ...

  8. redis在linux下的安装和配置

    1 Installation Download, extract and compile Redis with: $ wget http://download.redis.io/releases/re ...

  9. 网页静态化—redis | freemarker

    1.   学习计划 1.商品详情页面展示,动态展示 jsp + redis 2.使用freemarker实现网页静态化 3.ActiveMq同步生成静态网页 两个方案对比,方案一依赖web容器,red ...

  10. 为什么ajax发GET请求就成功,POST请求则报错?

    为什么ajax发GET请求就成功,POST请求则报错? (1)首先说明的是请求的URL是一个静态资源,比如一个json文件.(2)现在现象是发GET请求就成功,POST请求则报错.(3)最新发现:就算 ...