面向对象编程设计模式------享元模式

  所谓享元模式就是运行共享技术有效地支持大量细粒度对象的复用。系统使用少量对象,而且这些都比较相似,状态变化小,可以实现对象的多次复用。
  共享模式是支持大量细粒度对象的复用,所以享元模式要求能够共享的对象必须是细粒度对象。
  在了解享元模式之前我们先要了解两个概念:内部状态、外部状态。
  内部状态:在享元对象内部不随外界环境改变而改变的共享部分。
  外部状态:随着环境的改变而改变,不能够共享的状态就是外部状态。
  由于享元模式区分了内部状态和外部状态,所以我们可以通过设置不同的外部状态使得相同的对象可以具备一些不同的特性,而内部状态设置为相同部分。在我们的程序设计过程中,我们可能会需要大量的细粒度对象来表示对象,如果这些对象除了几个参数不同外其他部分都相同,这个时候我们就可以利用享元模式来大大减少应用程序当中的对象。如何利用享元模式呢?这里我们只需要将他们少部分的不同的部分当做参数移动到类实例的外部去,然后在方法调用的时候将他们传递过来就可以了。这里也就说明了一点:内部状态存储于享元对象内部,而外部状态则应该由客户端来考虑。

  享元模式结构:

  享元模式存在如下几个角色:
  Flyweight:抽象享元类。所有具体享元类的超类或者接口,通过这个接口,Flyweight可以接受并作用于外部专题;
  ConcreteFlyweight:具体享元类。指定内部状态,为内部状态增加存储空间。
  UnsharedConcreteFlyweight:非共享具体享元类。指出那些不需要共享的Flyweight子类。
  FlyweightFactory:享元工厂类。用来创建并管理Flyweight对象,它主要用来确保合理地共享Flyweight,当用户请求一个Flyweight时,FlyweightFactory就会提供一个已经创建的Flyweight对象或者新建一个(如果不存在)。
  享元模式的核心在于享元工厂类,享元工厂类的作用在于提供一个用于存储享元对象的享元池,用户需要对象时,首先从享元池中获取,如果享元池中不存在,则创建一个新的享元对象返回给用户,并在享元池中保存该新增对象。

public class FlyweightFactory {
    // 享元对象池
    private HashMap flyWeights = new HashMap();
    
    public FlyWeight getFlyWeight(String key) {
        // 享元池中存在享元对象,则直接返回
        if(flyWeights.containsKey(key)) {
            return (FlyWeight) flyWeights.get(key);
            
        } else {
            // 享元池中不存在享元对象,并将它放到享元池中
            FlyWeight fw = new ConcreteFlyweight();
            flyWeights.put(key, fw);
            return fw;
        }
    }
}

  实例场景:假如我们有一个绘图的应用程序,通过它我们可以出绘制各种各样的形状、颜色的图形,那么这里形状和颜色就是内部状态了,通过享元模式我们就可以实现该属性的共享了。另外在抽象出一个外部状态即绘图的用户,也就是说绘图的用户对象是不可以共享的,但是当大量用户绘制了相同属性(享元对象内部状态)的对象时,可以返回同一个引用,从而减少系统的对象数量。代码实现如下:

public class User {
    private String name;
    
    public User(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

/**
 * @Description: 抽象形状类,只有一个绘制图型的抽象方法,使用该方法需要传递一个用户对象
 * @author: zxt
 * @time: 2018年7月9日 上午9:43:41
 */
public abstract class Shape {
    public abstract void draw(User user);
}

/**
 * @Description: 绘制图形的具体类
 * @author: zxt
 * @time: 2018年7月9日 上午9:46:16
 */
public class Circle extends Shape {
    // 享元类的内部状态
    private String color;
    
    public Circle(String color) {
        this.color = color;
    }
    
    @Override
    public void draw(User user) {
        System.out.println("用户:" + user.getName() + ",画了一个  '" + color + "' 的圆形!");
    }
}

public class CircleFactory {
    // 享元对象池
    private static HashMap shapes = new HashMap();
    
    public static Shape getShape(String key) {
        // 享元池中存在享元对象,则直接返回
        if(shapes.containsKey(key)) {
            return (Shape) shapes.get(key);

        } else {
            // 享元池中不存在享元对象,并将它放到享元池中
            Shape shape = new Circle(key);
            shapes.put(key, shape);
            return shape;
        }
    }
    
    public static int getShapesNum() {
        return shapes.size();
    }
}

public class Client {

    public static void main(String[] args) {
        Shape shape1 = CircleFactory.getShape("红色");
        shape1.draw(new User("张三"));
        
        Shape shape2 = CircleFactory.getShape("灰色");
        shape2.draw(new User("李四"));
        
        Shape shape3 = CircleFactory.getShape("绿色");
        shape3.draw(new User("王五"));
        
        Shape shape4 = CircleFactory.getShape("红色");
        shape4.draw(new User("赵四"));
        
        Shape shape5 = CircleFactory.getShape("灰色");
        shape5.draw(new User("前乾"));
        
        Shape shape6 = CircleFactory.getShape("灰色");
        shape6.draw(new User("孙李"));
        
        System.out.println("一共创建了:" + CircleFactory.getShapesNum() + " 个图形对象!");
    }
}

  模式优缺点
  优点
  1、享元模式的优点在于它能够极大的减少系统中对象的个数。
  2、享元模式由于使用了外部状态,外部状态相对独立,不会影响到内部状态,所以享元模式使得享元对象能够在不同的环境被共享。
  缺点
  1、由于享元模式需要区分外部状态和内部状态,使得应用程序在某种程度上来说更加复杂化了。
  2、为了使对象可以共享,享元模式需要将享元对象的状态外部化,而读取外部状态使得运行时间变长。

  模式适用场景
  1、如果一个系统中存在大量的相同或者相似的对象,由于这类对象的大量使用,会造成系统内存的耗费,可以使用享元模式来减少系统中对象的数量。
  2、对象的大部分状态都可以外部化,可以将这些外部状态传入对象中。
  3、String对象的常量池,以及Integer等包装类的缓存策略:Integer.valueOf(int i)等都使用了享元模式。

  模式总结
  1、享元模式可以极大地减少系统中对象的数量。但是它可能会引起系统的逻辑更加复杂化。
  2、享元模式的核心在于享元工厂,它主要用来确保合理地共享享元对象。
  3、内部状态为不变共享部分,存储于享元对象内部,而外部状态是可变部分,它应当由客户端来负责。

你可能感兴趣的:(面向对象编程设计模式------享元模式)