一聚教程网:一个值得你收藏的教程网站

最新下载

热门教程

Java设计模式之享元模式学习笔记

时间:2016-04-08 编辑:简简单单 来源:一聚教程网

享元模式(Flyweight Pattern)主要用于减少创建对象的数量,以减少内存占用和提高性能。这种这种类型的设计模式属于结构型模式,它提供了减少对象数量从而改善应用所需的对象结构的方式。享元模式尝试重用现有的同类对象,如果未找到匹配的对象,则创建新对象。

Java中String的实现就是一个典型的享元模式应用,Java中的String存在字符串常量池中,Java会确保一个字符串常量在常量池中只有一个拷贝。数据库连接池也是一个比较电信的享元模式应用,可简单理解为先初始化一定数量的数据库连接,当需要获取连接的时候从连接池中去获得一个可用连接,当不存在可用连接时,再去扩展连接池的连接数量。


Flyweight在拳击比赛中指最轻量级,即“蝇量级”或“雨量级”,这里选择使用“享元模式”的意译,是因为这样更能反映模式的用意。享元模式是对象的结构模式。享元模式以共享的方式高效地支持大量的细粒度对象。

 

享元模式的结构

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

  一个内蕴状态是存储在享元对象内部的,并且是不会随环境的改变而有所不同。因此,一个享元可以具有内蕴状态并可以共享。

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

  享元模式可以分成单纯享元模式和复合享元模式两种形式。

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

1.jpg

 

在Flyweight模式中,由于要产生各种各样的对象,所以在Flyweight(享元)模式中常出现Factory模式。Flyweight的内部状态是用来共享的,Flyweight factory负责维护一个对象存储池(Flyweight Pool)来存放内部状态的对象。Flyweight模式是一个提高程序效率和性能的模式,会大大加快程序的运行速度.应用场合很多,下面举个例子:

先定义一个抽象的Flyweight类:

package Flyweight;
public abstract class Flyweight{
 public abstract void operation();
}

实现一个具体类:


package Flyweight;
public class ConcreteFlyweight extends Flyweight{
 private String string;
 public ConcreteFlyweight(String str){
  string = str;
 }
 public void operation()
 {
  System.out.println("Concrete---Flyweight : " + string);
 }
}

实现一个工厂方法类:


package Flyweight;
import java.util.Hashtable;
public class FlyweightFactory{
 private Hashtable flyweights = new Hashtable();//----------------------------1
 public FlyweightFactory(){}
 public Flyweight getFlyWeight(Object obj){
  Flyweight flyweight = (Flyweight) flyweights.get(obj);//----------------2
  if(flyweight == null){//---------------------------------------------------3
   //产生新的ConcreteFlyweight
   flyweight = new ConcreteFlyweight((String)obj);
   flyweights.put(obj, flyweight);//--------------------------------------5
  }
  return flyweight;//---------------------------------------------------------6
 }
 public int getFlyweightSize(){
  return flyweights.size();
 }
}这个工厂方法类非常关键,这里详细解释一下:
  在1处定义了一个Hashtable用来存储各个对象;在2处选出要实例化的对象,在6处将该对象返回,如果在Hashtable中没有要选择的对象,此时变量flyweight为null,产生一个新的flyweight存储在Hashtable中,并将该对象返回。
  最后看看Flyweight的调用:

 

package Flyweight;
import java.util.Hashtable;
public class FlyweightPattern{
 FlyweightFactory factory = new FlyweightFactory();
 Flyweight fly1;
 Flyweight fly2;
 Flyweight fly3;
 Flyweight fly4;
 Flyweight fly5;
 Flyweight fly6;
 /** *//** Creates a new instance of FlyweightPattern */
 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();
 }
}

下面是运行结果:


Concrete---Flyweight : Google
Concrete---Flyweight : Qutr
Concrete---Flyweight : Google
Concrete---Flyweight : Google
Concrete---Flyweight : Google
Concrete---Flyweight : Google
objSize = 2
 我们定义了6个对象,其中有5个是相同的,按照Flyweight模式的定义“Google”应该共享一个对象,在实际的对象数中我们可以看出实际的对象却是只有2个。

总结:
  Flyweight(享元)模式是如此的重要,因为它能帮你在一个复杂的系统中大量的节省内存空间。在JAVA语言中,String类型就是使用了享元模式。String对象是final类型,对象一旦创建就不可改变。在JAVA中字符串常量都是存在常量池中的,JAVA会确保一个字符串常量在常量池中只有一个拷贝。String a="abc",其中"abc"就是一个字符串常量。

熟悉java的应该知道下面这个例子:


String a = "hello";
String b = "hello";
if(a == b)
 System.out.println("OK");
else
 System.out.println("Error");输出结果是:OK。可以看出if条件比较的是两a和b的地址,也可以说是内存空间

核心总结,可以共享的对象,也就是说返回的同一类型的对象其实是同一实例,当客户端要求生成一个对象时,工厂会检测是否存在此对象的实例,如果存在那么直接返回此对象实例,如果不存在就创建一个并保存起来,这点有些单例模式的意思。通常工厂类会有一个集合类型的成员变量来用以保存对象,如hashtable,vector等。在java中,数据库连接池,线程池等即是用享元模式的应用。

 

下面是一个关于工厂模式、享元模式结合使用的例子:

// 创建Shape接口,并定义一个绘制方法

public interface Shape {
  void draw();
}
// 创建Circle类来实现Shape接口,定义Circle的相关属性,并且实现绘制(draw)方法。
public class Circle implements Shape {
  private String color;
  private int x;
  private int y;
  private int radius;

  public Circle(String color) {
    this.color = color;
  }

  public void setX(int x) {
    this.x = x;
  }

  public void setY(int y) {
    this.y = y;
  }

  public void setRadius(int radius) {
    this.radius = radius;
  }

  @Override
  public void draw() {
    System.out.println("Circle: Draw() [Color : " + color + ", x : " + x + ", y :" + y + ", radius :" + radius);
  }
}
import java.util.HashMap;

// 创建一个ShapeFactory工厂类,通过HashMap来存储对象,通过getCircle方法从map中取得现有对象或生成新的对象

public class ShapeFactory {
  private static final HashMapcircleMap = new HashMap();

  public static Shape getCircle(String color) {
    Circle circle = (Circle) circleMap.get(color);

    if (circle == null) {
      circle = new Circle(color);
      circleMap.put(color, circle);
      System.out.println("Creating circle of color : " + color);
    }
    return circle;
  }
}
// 随机输入color参数,从factory中获得对象
public class FlyweightPatternDemo {
  private static final String colors[] = { "Red", "Green", "Blue", "White", "Black" };

  public static void main(String[] args) {

    for (int i = 0; i < 20; ++i) {
      Circle circle = (Circle) ShapeFactory.getCircle(getRandomColor());
      circle.setX(getRandomX());
      circle.setY(getRandomY());
      circle.setRadius(100);
      circle.draw();
    }
  }

  private static String getRandomColor() {
    return colors[(int) (Math.random() * colors.length)];
  }

  private static int getRandomX() {
    return (int) (Math.random() * 100);
  }

  private static int getRandomY() {
    return (int) (Math.random() * 100);
  }

热门栏目