享元模式
定义:享元模式(英语:Flyweight Pattern)是一种软件设计模式。它使用共享物件,用来尽可能减少内存使用量以及分享资讯给尽可能多的相似物件;它适合用于当大量物件只是重复因而导致无法令人接受的使用大量内存。通常物件中的部分状态是可以分享。常见做法是把它们放在外部数据结构,当需要使用时再将它们传递给享元。
在享元模式中可以共享的相同内容称为 内部状态(Intrinsic State),而那些需要外部环境来设置的不能共享的内容称为 外部状态(Extrinsic State),其中外部状态和内部状态是相互独立的,外部状态的变化不会引起内部状态的变化。由于区分了内部状态和外部状态,因此可以通过设置不同的外部状态使得相同的对象可以具有一些不同的特征,而相同的内部状态是可以共享的。也就是说,享元模式的本质是分离与共享 : 分离变与不变,并且共享不变。把一个对象的状态分成内部状态和外部状态,内部状态即是不变的,外部状态是变化的;然后通过共享不变的部分,达到减少对象数量并节约内存的目的。
在享元模式中通常会出现工厂模式,需要创建一个享元工厂来负责维护一个享元池(Flyweight Pool)(用于存储具有相同内部状态的享元对象)。在享元模式中,共享的是享元对象的内部状态,外部状态需要通过环境来设置。在实际使用中,能够共享的内部状态是有限的,因此享元对象一般都设计为较小的对象,它所包含的内部状态较少,这种对象也称为 细粒度对象。 享元模式的目的就是使用共享技术来实现大量细粒度对象的复用。
拿棋子来举个例子,棋子颜色,重量,材质等等是不变的,就可以作为内部状态;棋子在棋盘的位置是变化的,为其外部状态。现在假设我们需求是这样的,展示下图棋盘的信息。
按照传统的方法,我们应该建这样一个类。
package com.example.demo.flyweight; public class StoneState { private String colour; private String weight; private String texture; private int x; private int y; public void display(){ System.out.println(toString()); } @Override public String toString() { return "StoneState{" + "colour='" + colour + '\'' + ", weight='" + weight + '\'' + ", texture='" + texture + '\'' + ", x=" + x + ", y=" + y + '}'; } }
这样的话,如果棋盘上的棋子增多,那么我们需要新建的对象会越多。如果使用享元模式,我们可以这样做
package com.example.demo.flyweight; public interface StoneInterface { void display(int x,int y); }
package com.example.demo.flyweight; public class StoneState1 implements StoneInterface{ private String colour; private String weight; private String texture; public StoneState1(String colour, String weight, String texture) { System.out.println("创建棋子"); this.colour = colour; this.weight = weight; this.texture = texture; } @Override public void display(int x, int y) { System.out.println("colour="+colour+",weight="+weight+",texture="+texture+",x=" + x+",y=" + y); } }
package com.example.demo.flyweight; import java.util.HashMap; import java.util.Map; public class StoneFactory { private Map<String, StoneInterface> map; private static StoneFactory stoneFactory = new StoneFactory(); public StoneFactory() { this.map = new HashMap<>(); } public static StoneFactory getStoneFactory() { return stoneFactory; } public StoneInterface getstone(String colour) { if (map.containsKey(colour)) { return map.get(colour); } else { StoneInterface s = new StoneState1(colour, "100g", "石英"); map.put(colour, s); return s; } } }
测试代码
package com.example.demo.flyweight; public class Client { public static void main(String[] args) { StoneFactory sf = StoneFactory.getStoneFactory(); StoneInterface s1 = sf.getstone("黑色"); s1.display(12,14); StoneInterface s2 = sf.getstone("黑色"); s2.display(12,13); StoneInterface s3 = sf.getstone("黑色"); s3.display(12,11); StoneInterface s4 = sf.getstone("白色"); s4.display(12,14); StoneInterface s5 = sf.getstone("白色"); s5.display(12,13); StoneInterface s6 = sf.getstone("白色"); s6.display(12,11); } }
执行测试代码,可以看到创建棋子的构造方法执行了两次,我们只创建了两种颜色的棋子(如果棋子的重量、材质都一样的话,我们还可以把这两个属性提取为内部状态,这样有能节约一个对象的空间)。
享元模式的核心就是把不变的内部状态共享,来达到节省内存的目的。