一 . 模式动机
- 面向对象技术可以很好的解决一个写灵活性或者扩展性问题,单在很多情况下需要在系统中增加类和对象的个数。当对象数量太多时,将导致运行代缴过高,带来性能下降。
- 享元模式通过共享技术实现相同或相似对象的重用。
- 享元模式中可以共享的相同内容成为内部状态,而那些需要外部环境来设置不能共享的内容成为外部状态。
- 享元模式中通常会出现工厂模式,需要创建一个享元工厂来负责维护一个享元池用于存储具有相同内部状态的享元对象,
二 . 模式定义
- 享元模式(Flyweight Pattern):运用共享技术有效的支持大量细粒度对象的复用,系统只是使用少量的对象,而这些对象都很相似,状态变化很小,可以实现对象的多次复用。由于享元模式要求能够共享对象必须是细粒度对象,因此又称为轻量级模式,是一种对象型结构模式。
三 . 模式结构
(图片来源于网络)
- Flyweight:抽象享元类
- ConcreteFlyweight:具体享元类
- UnsharedConceteFlyweight:非共享具体享元类
- FlyweightFactory:共享工厂类
四 . 模式分析
- 享元模式不会经常使用,因为他只能在特定条件下使用。
- 享元模式是一个考虑系统性能的设计模式,可以节省内存空间,提高系统性能。
- 享元模式的核心在于享元工厂,享元工厂类的作用在于提供一个用于存储享元对象的享元池,用户需要对象时,首先在享元池中获取,如果享元池中不存在,则创建一个新的享元对象返回给用户,并在享元池中保存该新增对象。
五 . 模式实例
-
围棋中的棋子就适合使用享元模式,一盘棋子需要大量的棋子,而棋子有共同特征就是非黑既白,而不同点就是棋子在棋盘的位置。
-
抽象享元类
public interface ChessFlyWeight {
void setcolor(String color);
String getcolor();
void display(Coordinate coordinate);
}
- 具体享元类(定义棋子的共同特征,颜色)
public class ConcrerteChess implements ChessFlyWeight {
private String color;
public ConcrerteChess(String color){
this.color = color;
}
@Override
public void setcolor(String color) {
this.color=color;
}
@Override
public String getcolor() {
return color;
}
@Override
public void display(Coordinate coordinate) {
System.out.println(this.color+":"+coordinate.getX()+","+coordinate.getY());
}
}
- 非共享具体享元类(定义棋子的非共同特征,位置)
public class Coordinate {
private int x;
private int y;
public Coordinate(int x, int y) {
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
}
- 享元工厂类
import java.util.HashMap;
import java.util.Map;
public class ChessFlyWeightFactory {
//享元池
private Map<String,ChessFlyWeight> map = new HashMap();
public ChessFlyWeight getChess(String color){
if(map.get(color)!=null){
return map.get(color);
}else {
ChessFlyWeight chessFlyWeight = new ConcrerteChess(color);
map.put(color,chessFlyWeight);
return chessFlyWeight;
}
}
}
- 测试类
public class Client {
public static void main(String[] args) {
ChessFlyWeightFactory chessFlyWeightFactory = new ChessFlyWeightFactory();
ChessFlyWeight concrerteChess1 = chessFlyWeightFactory.getChess("黑");
ChessFlyWeight concrerteChess2 = chessFlyWeightFactory.getChess("白");
System.out.println(concrerteChess1==concrerteChess2);
concrerteChess1.display(new Coordinate(1,1));
concrerteChess2.display(new Coordinate(1,2));
}
}
- 测试结果
E:\Java\jdk1.8.0_171\bin\java.exe "-javaagent:E:\idea\IntelliJ IDEA 2019.1.3\lib\idea_rt.jar=52146:E:\idea\IntelliJ IDEA 2019.1.3\bin" -Dfile.encoding=UTF-8 -classpath "E:\Java\jdk1.8.0_171\jre\lib\charsets.jar;E:\Java\jdk1.8.0_171\jre\lib\deploy.jar;E:\Java\jdk1.8.0_171\jre\lib\ext\access-bridge-64.jar;E:\Java\jdk1.8.0_171\jre\lib\ext\cldrdata.jar;E:\Java\jdk1.8.0_171\jre\lib\ext\dnsns.jar;E:\Java\jdk1.8.0_171\jre\lib\ext\jaccess.jar;E:\Java\jdk1.8.0_171\jre\lib\ext\jfxrt.jar;E:\Java\jdk1.8.0_171\jre\lib\ext\localedata.jar;E:\Java\jdk1.8.0_171\jre\lib\ext\nashorn.jar;E:\Java\jdk1.8.0_171\jre\lib\ext\sunec.jar;E:\Java\jdk1.8.0_171\jre\lib\ext\sunjce_provider.jar;E:\Java\jdk1.8.0_171\jre\lib\ext\sunmscapi.jar;E:\Java\jdk1.8.0_171\jre\lib\ext\sunpkcs11.jar;E:\Java\jdk1.8.0_171\jre\lib\ext\zipfs.jar;E:\Java\jdk1.8.0_171\jre\lib\javaws.jar;E:\Java\jdk1.8.0_171\jre\lib\jce.jar;E:\Java\jdk1.8.0_171\jre\lib\jfr.jar;E:\Java\jdk1.8.0_171\jre\lib\jfxswt.jar;E:\Java\jdk1.8.0_171\jre\lib\jsse.jar;E:\Java\jdk1.8.0_171\jre\lib\management-agent.jar;E:\Java\jdk1.8.0_171\jre\lib\plugin.jar;E:\Java\jdk1.8.0_171\jre\lib\resources.jar;E:\Java\jdk1.8.0_171\jre\lib\rt.jar;E:\Design pattern\out\production\Design pattern" FlyWeight享元模式.Client
false
黑:1,1
白:1,2
Process finished with exit code 0
六 . 模式优缺点
- 优点
- 极大减少内存中对象数量,使得相同对象或相似对象在内存中只保存一份。
- 享元对象可以在不同的环境中被共享。
- 缺点
- 使系统更加复杂,要分离出内部状态和外部状态,使得程序逻辑复杂化。
- 需要对享元的状态外部化,而读取外部状态使得运行时间变长。
七 . 使用环境
- 一个系统有大量相同或者相似对象。
- 对象的大部分状态都可以外部化。
- 使用享元模式需要维护一个享元池,需要耗费资源,因此,应当在多次重复使用享元对象时才值得使用享元模式。
八 . 模式扩展
- 单纯享元模式:所有享元对象都可以共享,不存在非共享具体享元类。
- 复合享元模式:将一些单纯享元使用组合模式加以组合,可以形成复合享元对象,这样的享元对象本身不能共享,单他们分解成单纯享元对象可以共享。
次重复使用享元对象时才值得使用享元模式。