GOF23设计模式之享元模式(flyweight)
一、享元模式概述
内存属于稀缺资源,不要随便浪费。如果有很多个完全相同或相似的对象,可以通过享元模式,节省内存。
享元模式核心:
(1)享元模式可以共享的、方式高效的支持大量细粒度对象的重用;
(2)享元对象能做到共享的关键是区分了内部状态和外部状态:
① 内部状态:可以共享,不会随环境变化而改变
② 外部状态:不能共享,会随环境变化而改变
二、享元模式实现
(1)FlyWeightFactory享元工厂类
创建并管理享元对象,享元池一般设计成键值对。
(2)FlyWeight抽象享元类
通常是一个接口,声明公共方法,这些方法可以向外界提供对象的内部状态,设置外部状态。
(3)ConcreteFlyWeight具体享元类
内部状态提供成员变量进行存储。
(4)UnShareConcreteFlyWeight非共享享元类
不能被共享的子类可以设计成非共享享元类。
三、元模式场景导入示例代码
场景:围棋中每一个棋子都是一个对象。棋子有如下属性:
颜色 状态 大小(这些是可以共享的)称之为:内部状态
位置(这是不可以共享的)称之为:外部状态
1 /** 2 * 抽象享元类FlyWeight 3 * @author CL 4 * 5 */ 6 public interface ChessFlyWeight { 7 void setColor(String color); 8 String getColor(); 9 void display(Coordinate c); 10 } 11 12 /** 13 * ConcreteFlyWeight具体享元类 14 * 内部状态 15 * @author CL 16 * 17 */ 18 class ConcreteChess implements ChessFlyWeight { 19 20 private String color; 21 22 public ConcreteChess(String color) { 23 this.color = color; 24 } 25 26 @Override 27 public void setColor(String color) { 28 this.color = color; 29 } 30 31 @Override 32 public String getColor() { 33 return color; 34 } 35 36 @Override 37 public void display(Coordinate c) { 38 System.out.println("棋子的颜色: "+color); 39 System.out.println("棋子的位置: ["+c.getX()+","+c.getY()+"]"); 40 } 41 42 }
1 import java.util.HashMap; 2 import java.util.Map; 3 4 /** 5 * 享元工厂类 6 * @author CL 7 * 8 */ 9 public class ChessFlyWeightFactory { 10 //享元池 11 private static Map<String, ChessFlyWeight> map = 12 new HashMap<String, ChessFlyWeight>();; 13 14 public static ChessFlyWeight getChess(String color) { 15 if (map.get(color) != null) { 16 return map.get(color); 17 } else { 18 ChessFlyWeight cfw = new ConcreteChess(color); 19 map.put(color, cfw); 20 return cfw; 21 } 22 } 23 24 }
1 /** 2 * 外部状态 3 * UnShareFlyWeight非共享享元类 4 * @author CL 5 * 6 */ 7 public class Coordinate { 8 private int x, y; 9 10 public Coordinate(int x, int y) { 11 this.x = x; 12 this.y = y; 13 } 14 15 public int getX() { 16 return x; 17 } 18 19 public void setX(int x) { 20 this.x = x; 21 } 22 23 public int getY() { 24 return y; 25 } 26 27 public void setY(int y) { 28 this.y = y; 29 } 30 }
测试:
1 /** 2 * 测试享元模式 3 * @author CL 4 * 5 */ 6 public class Client { 7 8 public static void main(String[] args) { 9 ChessFlyWeight c1 = ChessFlyWeightFactory.getChess("黑色"); 10 ChessFlyWeight c2 = ChessFlyWeightFactory.getChess("黑色"); 11 System.out.println(c1); 12 System.out.println(c2); //c1 和 c2 是同一对象 13 14 System.out.println("--------------------"); 15 c1.display(new Coordinate(10, 10)); 16 c2.display(new Coordinate(20, 20)); 17 } 18 19 }
控制台输出:
com.caolei.flyweight.ConcreteChess@759ebb3d com.caolei.flyweight.ConcreteChess@759ebb3d -------------------- 棋子的颜色: 黑色 棋子的位置: [10,10] 棋子的颜色: 黑色 棋子的位置: [20,20]
四、享元模式优缺点
优点:
(1)极大的减少内存中对象的数量;
(2)相同或相似对象内存中只存一份,极大的节约资源,提高系统性能;
(3)外部状态相对独立,不影响内部状态。
缺点:
(1)模式较复杂,使程序逻辑复杂化;
(2)为了节省内存,共享了内部状态,分离出外部状态,而读取外部状态使运行时间较长,用时间换空间。
五、享元模式常见开发应用场景
(1)享元模式由于其共享的特性,可以在任何“池”中操作,比如:线程池、数据库连接池;
(2)String类的设计也是享元模式;
(3)…………