23种设计模式之12.享元模式

94 阅读2分钟

享元模式是通过共享的模式减少构建对象的数量,从而减少资源的占用,每当我们需要一个对象时,工厂会尝试从缓存中获取,如果不存在则会创建对象并加入到缓存中,下次再需要同类对象时就直接从缓存中获取而不需要重新创建。

下面将通过生成不同水果的例子来理解享元模式。

//水果接口
public interface Fruit {
}
//水果实现类
public class FruitImpl implements Fruit{

    private String type;
    private String color;

    public FruitImpl(String type) {
        this.type = type;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    @Override
    public String toString() {
        return color+"的"+type;
    }

创建水果工厂来管理所有水果对象:

public class FruitFactory {

    //水果对象缓存集合,由水果类型进行标识,同一种水果类型约定为同一类相似对象,从而通过享元模式进行共享
    public static final Map<String,Fruit> fruitMap = new HashMap<>();

    public static synchronized Fruit getFruit(String type,String color){
        FruitImpl fruit = (FruitImpl)fruitMap.get(type);
        if (Objects.isNull(fruit)){
            fruit = new FruitImpl(type);
            fruitMap.put(type,fruit);
        }
        //实时查看当前工厂中有多少实际的对象
        System.out.println("当前工厂拥有对象数量:"+fruitMap.size());
        fruit.setColor(color);
        return fruit;
    }
}

创建测试方法进行测试:

public class Main {

    public static void main(String[] args) {
        FruitImpl fruit1 = (FruitImpl)FruitFactory.getFruit("苹果", "红色");
        System.out.println(fruit1);
        FruitImpl fruit2 = (FruitImpl)FruitFactory.getFruit("苹果", "绿色");
        System.out.println(fruit2);
        FruitImpl fruit3 = (FruitImpl)FruitFactory.getFruit("香蕉", "黄色");
        System.out.println(fruit3);
        FruitImpl fruit4 = (FruitImpl)FruitFactory.getFruit("香蕉", "绿色");
        System.out.println(fruit4);
        FruitImpl fruit5 = (FruitImpl)FruitFactory.getFruit("苹果", "青色");
        System.out.println(fruit5);
    }
}

程序运行结果:

image.png

我们从水果工厂中拿到了5种不同的水果,但是实际创建的对象只有两个,这就是共享的思维,所有同类型的水果都是同一个对象。

在java中的常量池也是采用的享元模式。看下面这个例子:

public class Main {
    public static void main(String[] args) {
        String s1 = "abc";
        String s2 = "abc";
        System.out.println(s1==s2);
        Integer i1 = Integer.valueOf(127);
        Integer i2 = Integer.valueOf(127);
        System.out.println(i1==i2);
        Integer i3 = Integer.valueOf(128);
        Integer i4 = Integer.valueOf(128);
        System.out.println(i3==i4);
    }
}

程序运行结果:

image.png

s1,s2都是字符串"abc",这种字符串常量都保存在常量池中,不管创建多少个"abc"的字符串对象所以他们都是指向同一个对象,结果固然是true;

i1,i2是同一个对象,i3,i4却不是同一个对象,这是因为Integer常量的范围是-128到127在这个范围内才是从常量池中获取,否则就是新创建的对象。

享元模式优点:

  1. 极大减少内存中对象的数量,可以节约系统资源,提高系统性能。

享元模式缺点:

  1. 提升系统复杂度,使用者需要分离出外部状态和内部状态,处理不好容易造成系统的混乱。