TypechoJoeTheme

IT技术分享

统计

享元模式(Flyweight)——23种设计模式之结构型模式

2016-04-20
/
0 评论
/
521 阅读
/
正在检测是否收录...
04/20

面向对象很好地解决了系统抽象性的问题,同时在大多数情况下,也不会损及系统的性能。 但在某些特殊的应用中,由于对象的数量太大, 采用面向对象会给系统带来难以承受的内存开销。比如图形应用中的图元等对象、字处理应 用中的字符对象等。

采用纯粹对象方案的问题在于大量细粒度的对 象会很快充斥在系统中,从而带来很高的运行时代价------主要指内存需求方面的代价。

如何在避免大量细粒度对象问题的同时,让外部客户程序仍然能够透明地使用面向对象的方式来进行操作?

当对象的粒度太小的时候,大量对象将会产生巨大的资源消耗,因此考虑用共享对象(flyweight)来实现逻辑上的大量对象。Flyweight模式主要解决面向对象的代价问题,一般不触及面向对象的抽象性问题。Flyweight对象可用于不同的context 中,本身固有的状态(内部状态)不随context发生变化,而其他的状态(外部状态)随context而变化

Flyweight模式对那些通常因为数量太大而难以用对象 来表示的概念或实体进行建模.

一、知识外延

相关补充

  • ①、Flyweight在拳击比赛中指最轻量级,即"蝇量级",有 些翻译为"羽量级"。这里使用"享元模式"更能反映模式 的用意。
  • ②、享元模式以共享的方式高效地支持大量的细粒度对象。 享元对象能做到共享的关键是区分内蕴状态(Internal State)和外蕴状态(External State)。内蕴状态是存储 在享元对象内部并且不会随环境改变而改变。因此内 蕴状态可以共享。
  • ③、外蕴状态是随环境改变而改变的、不可以共享的状态。 享元对象的外蕴状态必须由客户端保存,并在享元对 象被创建之后,在需要使用的时候再传入到享元对象 内部。外蕴状态与内蕴状态是相互独立的。

二、类图表示

 享元模式涉及到Flyweight(享元角色),ConcreteFlyweight(具体享元角色),UnsharedConcreteFlyweight(复合享元角色),FlyweightFactory(享员工厂角色),Client(客户端)等五个角色。

  • ①、Flyweight:描述一个接口,通过这个接口flyweight可以接受并作用于外部状态。
  • ②、ConcreteFlyweight:实现Flyweight接口,并为内部状态增加存储空间。必须是可以共享的,存储的状态必须是内部的。
  • ③、UnsharedConcreteFlyweight:不强制共享。
  • ④、FlyweightFactory:创建并管理flyweight对象,确保合理地共享flyweight,提供已创建的flyweight实例或者创建一个(如果不存在的话)
  • ⑤、Client:维持一个对flyweight的引用,计算或存储一个(多个)flyweight的外部状态

1、单纯享元模式:在单纯享元模式中,所有的享元对象 都是可以共享的。

 java-flyweight-model-2

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

java-flyweight-model-1

三、代码示例

//抽象的Flyweight类
public abstract class Flyweight{
    public abstract void operation();
}
//实现一个具体类
public class ConcreteFlyweight extends Flyweight{
    private String string;  //内部状态作为成员属性
    public ConcreteFlyweight(String str){
        string = str;
    }
    public void operation(){

        //外部状态不保存在享元对象中, 使用时由外部设置,即使是同一对象,每次调用时可传入不 同的外部状态
        System.out.println("Concrete---Flyweight : " + string);
    }
}
//实现一个工厂方法类
public class FlyweightFactory{
    private Hashtable flyweights = new Hashtable();
    public FlyweightFactory(){}
    public Flyweight getFlyWeight(Object obj){
        Flyweight flyweight = (Flyweight) flyweights.get(obj);
        if(flyweight == null){
            //产生新的ConcreteFlyweight
            flyweight = new ConcreteFlyweight((String)obj);
            flyweights.put(obj, flyweight);
        }
        return flyweight;
    }
    public int getFlyweightSize(){
        return flyweights.size();
    }
}
//测试
public class FlyweightPattern{
    FlyweightFactory factory = new FlyweightFactory();
    Flyweight fly1;
    Flyweight fly2;
    Flyweight fly3;
    Flyweight fly4;
    Flyweight fly5;
    Flyweight fly6;
    public FlyweightPattern(){
        fly1 = factory.getFlyWeight("Google");
        fly2 = factory.getFlyWeight("Qutr");
        fly3 = factory.getFlyWeight("Google");
        fly4 = factory.getFlyWeight("Google");
        fly5 = factory.getFlyWeight("Google");
        fly6 = factory.getFlyWeight("Google");
    }
    public void showFlyweight(){
        fly1.operation();
        fly2.operation();
        fly3.operation();
        fly4.operation();
        fly5.operation();
        fly6.operation();
        int objSize = factory.getFlyweightSize();
        System.out.println("objSize = " + objSize);
    }
    public static void main(String[] args){
        System.out.println("The FlyWeight Pattern!");
        FlyweightPattern fp = new FlyweightPattern();
        fp.showFlyweight();
    }
}

四、优缺点分析

1、优点

  • ①、它大幅度地降低内存中对象的数量。但是,它做到这一点所付出的代价也是很高的。
  • ②、享元模式并不是一个非常常见的模式,但在某些情况 下,享元模式可以成为代码重构的强大武器。

2、缺点

  • ①、享元模式使系统更加复杂。为了使对象可以共享,需要将一些状态外部化,这使得程序的逻辑复杂化。
  • ②、享元模式将享元对象的状态外部化,而读取外部状态 使得运行时间稍微变长。

五、模式间关系

1、与组合模式的关系

  • ①、与Composite模式结合起来,用共享叶节点的有向无环图实现一个逻辑上的层次结构
    2、与状态模式和策略模式的关系
  • ①、最好用Flyweight实现State和Strategy对象

六、应用场景

一个应用程序使用了大量的对象,完全由于使用大量的对象,造成很大的存储开销

  • ①、对象的大多数状态都可变为外部状态,这些对象可以按照内蕴状态分成很多的组。
  • ②、如果删除对象的外部状态,那么可以用相对较少的共享对象取代很多组对象。
  • ③、应用程序不依赖于对象标识,换言之,这些对象可以是不可分辨的。
  • ④、使用享元模式需要维护一个记录了系统已有的所有享元的表,而这需要耗费资源。因此,应当在有足够多的享元实例可供共享时才值得使用享元模式。

七、总结

Flyweight模式主要解决面向对象的代价问题

  • ①、Flyweight执行时所需的状态必定是内部或外部 的。内部状态存储在ConcreteFlyweight中,外部对象则由Client对象存储或计算。当用户调用 flyweight对象的操作时,将该状态传递给它。
  • ②、用户不应直接对ConcreteFlyweight类进行实例 化,而只能从FlyweightFactory对象得到 ConcreteFlyweight对象,以保证对它们适当地 进行共享。
  • ③、把对象的状态分开:intrinsic and extrinsic。内部状态的共享节约了大量空 间,外部状态可通过计算获得从而进一步节约空间
  • ④、删除外部状态:理想情况是,外部状态可以由一个单独的对象结构计算得到,且该结构的存储要求非常小。
  • ⑤、管理共享对象,客户不能直接实例化flyweight, 必须通过管理器,例如FlyweightFactory。
  • ⑥、Flyweight采用对象共享的做法来降低系统中对象的个数, 从而降低细粒度对象给系统带来的内存压力。在具体实现方面,要注意对象状态的处理。
  • ⑦、对象的个数太大从而导致对象内存开销加大----什么样的 数量才算大?需要我们仔细地根据具体应用情况进行评 估,而不能凭空臆断。
朗读
赞 · 0
版权属于:

IT技术分享

本文链接:

https://idunso.com/archives/1861/(转载时请注明本文出处及文章链接)