Java设计模式之享元模式

亨元模式(Flyweight Pattern): 如想让某个类的一个实例能用来提供许多”虚拟实例”, 就使用蝇量模式。

享元模式的详解
享元模式采用一个共享来避免大量拥有相同内容对象的开销。这种开销最常见、最直观的就是内存的消耗。享元对象能做到共享的关键是区分内蕴状态外蕴状态

内蕴状态:是指存储在享元对象内部的,并且是不会随着环境的改变而改变的。所以享元对象可以具有内蕴状态,并可以被共享。

外蕴状态:是随环境的改变而改变的,不可以共享的。享元对象的外蕴状态必须由客户端保存,并在享元对象呗创建之后,在需要使用的时候在传入到享元对象内部。外蕴状态不可以影响享元对象的内蕴状态,他们是互相独立的。

享元模式有可以分为:单纯享元模式和符合享元模式

单纯享元模式

类图:
这里写图片描述

角色说明:
抽象享元角色(Flyweight):给出一个抽象接口,以规定出所有具体享元角色需要实现的方法。

具体享元角色(ConcreteFlyweight):实现抽象享元角色所规定的出的接口。如果有内蕴状态的话,必须负责为内蕴状态提供存储空间。

享元工厂角色(FlyweightFactory):本角色负责创建和管理享元角色。享元工程必须保证享元对象可以被系统适当地共享。当一个客户端对象调用一个享元对象的时候,享元工程角色会检查系统中是否已经有一个符合要求的享元对象。如果已经有了,享元工厂就应当提供这个已有的享元对象;如果系统中没有一个适当的享元对象的话,享元工厂就应当创建一个合适的享元对象。

代码演示,抽象享元类:

public interface Flyweight {
    // 示例操作方法,参数state是外蕴状态
    public void operation(String state);
}

具体享元类:

public class ConcreteFlyweight implements Flyweight {
    private String intrinsicState = null;

    // 内蕴状态在构造时作为参数传入,传入之后是不可改变的
    public ConcreteFlyweight(String state) {
        this.intrinsicState = state;
    }

    // 外蕴状态作为参数传入方法中,改变方法的行为, 但是并不改变对象的内蕴状态。
    @Override
    public void operation(String state) {
        System.out.println("内蕴状态: " + this.intrinsicState);
        System.out.println("外蕴状态: " + state);
    }

}

享元工厂类:

public class FlyweightFactory {
    //对象的内蕴状态是不变的,可以作为该对象的标识
    private Map<String, Flyweight> resource = new HashMap<String, Flyweight>();

    public Flyweight factory(String state) {
        // 先从缓存中查找对象,查看是否有适合的对象进行分享
        Flyweight fly = resource.get(state);
        if (fly == null) {
            // 如果没有就应当创建一个合适的Flyweight对象
            fly = new ConcreteFlyweight(state);
            // 把这个新创建的Flyweight对象添加到缓存中
            resource.put(state, fly);
            System.out.println(state + "状态对应对象不存在,已经新建一个新的对象");
        } else {
            System.out.println(state + "状态对应对象已经存在,已从缓存中取出");
        }
        return fly;
    }
}

客户端测试类:

public class Test {

    public static void main(String[] args) {

        FlyweightFactory factory = new FlyweightFactory();
        Flyweight fly = factory.factory("NBA");
        fly.operation("Request one");
        System.out.println("=============");

        fly = factory.factory("CBA");
        fly.operation("Request two");
        System.out.println("=============");

        fly = factory.factory("NBA");
        fly.operation("Request three");
    }

}

运行结果:
这里写图片描述




复合享元模式

在单纯享元模式中,所有的享元对象都是单纯享元对象,都可以直接进行共享。还有另一种较为复杂的情况,将一些单纯享元对象复合起来,形成复合享元对象。这种复合享元对象本身是不能共享的,但是可以分解成单纯享元对象,然后进行单纯享元对象的分享。

类图:
这里写图片描述

角色说明:
复合享元角色(CompositeFlyweight):复合享元角色所代表第的对象是不可以共享的,但是一个复合享元对象可以分解成为多个单纯享元对象的组合。复合享元角色是不可共享的享元对象。

剩余的角色和单纯享元对象角色说明一样。

代码演示,抽象享元类:

public interface Flyweight {
    // 示例操作方法,参数state是外蕴状态
    public void operation(String state);
}

具体享元类:

public class ConcreteFlyweight implements Flyweight {
    private String intrinsicState = null;

    // 内蕴状态在构造时作为参数传入,传入之后是不可改变的
    public ConcreteFlyweight(String state) {
        this.intrinsicState = state;
    }

    // 外蕴状态作为参数传入方法中,改变方法的行为, 但是并不改变对象的内蕴状态。
    @Override
    public void operation(String state) {
        System.out.println("内蕴状态: " + this.intrinsicState);
        System.out.println("外蕴状态: " + state);
    }

}

复合享元类:

public class CompositeFlyweight implements Flyweight {

    private Map<String, Flyweight> compositeResource = new HashMap<String, Flyweight>();

    // 复合一个新的单纯享元对象
    public void add(String key, Flyweight fly) {
        compositeResource.put(key, fly);
    }

    // 外蕴状态作为参数传入到方法中
    @Override
    public void operation(String state) {
        Flyweight fly = null;
        for (Object object : compositeResource.keySet()) {
            fly = compositeResource.get(object);
            fly.operation(state);
        }

    }

}

复合享元对象是由单纯享元对象通过复合而成的,所以有add()这个复合方法。由于复合对象是由不同的子元素,而这些子元素是在复合享元对象创建之后才加入的。这就意味着复合享元对象的内蕴状态是随时会改变的,所以它不能共享。

享元工厂类:

public class FlyweightFactory {
    //对象的内蕴状态是不变的,可以作为该对象的标识
    private Map<String, Flyweight> resource = new HashMap<String, Flyweight>();

    //复合享元的工厂方法
    public Flyweight factory(List<String> compositeState) {  
        CompositeFlyweight compositeFlyweight = new CompositeFlyweight();  

        for (String state : compositeState) {  
            compositeFlyweight.add(state, this.factory(state));  
        }  

        return compositeFlyweight;  
    }  

    public Flyweight factory(String state) {
        // 先从缓存中查找对象,查看是否有适合的对象进行分享
        Flyweight fly = resource.get(state);
        if (fly == null) {
            // 如果没有就应当创建一个合适的Flyweight对象
            fly = new ConcreteFlyweight(state);
            // 把这个新创建的Flyweight对象添加到缓存中
            resource.put(state, fly);
            System.out.println(state + "状态对应对象不存在,已经新建一个新的对象");
        } else {
            System.out.println(state + "状态对应对象已经存在,已从缓存中取出");
        }
        return fly;
    }
}

客户端测试类:

public class Test {
    public static void main(String[] args) {
         List<String > compositeState = new ArrayList<String>();  
          compositeState.add("NBA");  
          compositeState.add("CBA");  
          compositeState.add("NBA");  

          FlyweightFactory flyFactory = new FlyweightFactory();  
          Flyweight compositeFly1 = flyFactory.factory(compositeState);  
          System.out.println("================");
          Flyweight compositeFly2 = flyFactory.factory(compositeState);  
          compositeFly1.operation("Request");  

          System.out.println("================");  
          System.out.println("复合享元角色是否可以共享:" + (compositeFly1 == compositeFly2));  
          System.out.println("================");  
          String state = "CBA";  
          Flyweight fly1 = flyFactory.factory(state);  
          Flyweight fly2 = flyFactory.factory(state);  
          System.out.println("单纯享元角色是否可以共享:" + (fly1 == fly2));  
    }
}

运行结果:
这里写图片描述

享元模式的优点:

  • 享元模式可以很好的降低内存中对象的数量,从而有效的减少内存的消耗。

缺点:

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

享元模式的适用场景:

  • 一个系统中存在着大量细粒度的对象;
  • 大量细粒度的对象耗费了大量的内存;
  • 大量细粒度的对象状态中的大部分都可以外部化;
  • 大量细粒度的对象可以按照内蕴状态分成很多组,当把外蕴对象从对象中剔除时,每一个组都可以仅用一个对象代替;
  • 系统不依赖于这些对象的身份,换而言之,这些对象允许不用分辨。

题外话:其实Java中的String类型就使用了享元模式。下面看个简单的例子

这里写图片描述

这说明了str和str1两个对象引用的是常量池中的同一个字符串常量”Layne”。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值