定义
享元模式(Flyeight):运用共享技术有效的支持大量细颗粒度的对象。
简单来说就是,共享对象、重复利用对象。
另外,享元模式还有四个角色、两个状态。
四个角色:
- 抽象享元角色(Flyweight):是所有的具体享元类的基类,为具体享元规范需要实现的公共接口,非享元的外部状态以参数的形式通过方法传入。
- 具体享元(Concrete Flyweight)角色:实现抽象享元角色中所规定的接口。
- 非享元(Unsharable Flyweight)角色:是不可以共享的外部状态,它以参数的形式注入具体享元的相关方法中。
- 享元工厂(Flyweight Factory)角色:负责创建和管理享元角色。当客户对象请求一个享元对象时,享元工厂检査系统中是否存在符合要求的享元对象,如果存在则提供给客户;如果不存在的话,则创建一个新的享元对象。
两个状态:
- 内部状态:即不会随着环境的改变而改变的可共享部分。比如棋子的类型,不会随着环境改变而改变。
- 外部状态,指随环境改变而改变的不可以共享的部分。比如棋子的位置,随着人下的位置而改变。
享元模式的UML类图:
Java模式井字棋游戏
分析
需求明确了,咱们来分析一波。
下棋需要棋盘和棋子,这些都是井字棋的东西。——井字棋Tictactoe可作为接口或者抽象类
棋盘可以显示自己可下部分的编号——棋盘Chessboard需要有二维数组作为棋盘,并且作为共享部分
棋子类型有两种×、⭕(我输入的是朴实无华的圆圈,但是CSDN直接给变成这样了),两外棋子有自己的位置——棋子的类型(ChessPieces)是内部状态,棋子的位置是外部状态(ChessPiecesSite)需要分开写
再写一个工厂类作为池,负责给客户端提供棋盘和棋子
UML类图
代码
Tictactoe——抽象享元角色(Flyweight)
// Tictactoe——井字棋
public interface Tictactoe {
void show(ChessPiecesSite chessPiecesSite);
}
Chessboard——具体享元角色(Concrete Flyweight)
public class Chessboard implements Tictactoe{
public int[][] tic = {
{1,2,3},
{4,5,6},
{7,8,9}
};
@Override
public void show(ChessPiecesSite chessPiecesSite) {
System.out.println("*****");
System.out.println("获得棋盘,棋盘对应编号如下:");
for (int i = 0; i < tic.length; i++) {
for (int j = 0; j < tic.length; j++) {
System.out.print(tic[i][j]);
}
System.out.println();
}
System.out.println("*****");
}
}
Tictactoe——抽象享元角色(Flyweight)
public class ChessPieces implements Tictactoe{
// 该棋子的类型
public String type;
public ChessPieces(String type) {
super();
this.type = type;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
@Override
public void show(ChessPiecesSite chessPiecesSite) {
System.out.println("棋局编号:"+chessPiecesSite.getChessboardID());
System.out.println("棋子类型:"+type);
System.out.println("棋子位置:"+chessPiecesSite.getSite());
System.out.println("---");
}
}
ChessPiecesSite—— 非享元角色(Unsharable Flyweight)
public class ChessPiecesSite implements Tictactoe{
// 该棋子所属棋局id
public int chessboardID;
// 该棋子的位置
public int site;
public ChessPiecesSite(int chessboardID, int site) {
super();
this.chessboardID = chessboardID;
this.site = site;
}
public int getChessboardID() {
return chessboardID;
}
public void setChessboardID(int chessboardID) {
this.chessboardID = chessboardID;
}
public int getSite() {
return site;
}
public void setSite(int site) {
this.site = site;
}
@Override
public void show(ChessPiecesSite chessPiecesSite) {
System.out.println("所属棋盘"+chessboardID);
System.out.println("棋子位置:"+site);
}
}
TictactoeFactory——享元工厂角色(Flyweight Factory)
import java.util.HashMap;
import java.util.Map;
public class TictactoeFactory {
private static Map<String,Tictactoe> pool = new HashMap<>();
/**
* 获取棋盘对象
* @param chessboard
* @return
*/
public static Tictactoe getChessboard(String chessboard) {
if(pool.get(chessboard)!=null) {
return pool.get(chessboard);
}
pool.put(chessboard, new Chessboard());
return pool.get(chessboard);
}
/**
* 获取棋子对象
* @param type
* @return
*/
public static Tictactoe getChessPieces(String type) {
if(pool.get(type)!=null) {
return pool.get(type);
}
pool.put(type, new ChessPieces(type));
return pool.get(type);
}
/**
* 获取棋子对象
* @return
*/
public static int getPoolSize() {
return pool.size();
}
}
Client——客户端
public class Client {
public static void main(String[] args) {
Tictactoe chessboard = TictactoeFactory.getChessboard("chessboard");
chessboard.show(null);
Tictactoe chessPieces1 = TictactoeFactory.getChessPieces("×");
chessPieces1.show(new ChessPiecesSite(1,5));
Tictactoe chessPieces2 = TictactoeFactory.getChessPieces("⭕");
chessPieces2.show(new ChessPiecesSite(1,2));
Tictactoe chessPieces3 = TictactoeFactory.getChessPieces("×");
chessPieces3.show(new ChessPiecesSite(1,7));
Tictactoe chessPieces4 = TictactoeFactory.getChessPieces("⭕");
chessPieces4.show(new ChessPiecesSite(1,3));
Tictactoe chessPieces5 = TictactoeFactory.getChessPieces("×");
chessPieces5.show(new ChessPiecesSite(1,1));
System.out.println("池大小:"+TictactoeFactory.getPoolSize());
System.out.println("持⭕的玩家输了~~重新开始一局!!");
Tictactoe chessboard2 = TictactoeFactory.getChessboard("chessboard");
chessboard2.show(null);
Tictactoe chessPieces21 = TictactoeFactory.getChessPieces("×");
chessPieces21.show(new ChessPiecesSite(2,5));
Tictactoe chessPieces22 = TictactoeFactory.getChessPieces("⭕");
chessPieces22.show(new ChessPiecesSite(2,6));
Tictactoe chessPieces23 = TictactoeFactory.getChessPieces("×");
chessPieces23.show(new ChessPiecesSite(2,7));
Tictactoe chessPieces24 = TictactoeFactory.getChessPieces("⭕");
chessPieces24.show(new ChessPiecesSite(2,3));
Tictactoe chessPieces25 = TictactoeFactory.getChessPieces("×");
chessPieces25.show(new ChessPiecesSite(2,9));
System.out.println("池大小:"+TictactoeFactory.getPoolSize());
System.out.println("持⭕的玩家又输了!!");
}
}
/ 我的圆圈本来是朴实无华的。。。。
像这样:
运行结果
*****
获得棋盘,棋盘对应编号如下:
123
456
789
*****
棋局编号:1
棋子类型:×
棋子位置:5
---
棋局编号:1
棋子类型:⭕
棋子位置:2
---
棋局编号:1
棋子类型:×
棋子位置:7
---
棋局编号:1
棋子类型:⭕
棋子位置:3
---
棋局编号:1
棋子类型:×
棋子位置:1
---
池大小:3
持⭕的玩家输了~~重新开始一局!!
*****
获得棋盘,棋盘对应编号如下:
123
456
789
*****
棋局编号:2
棋子类型:×
棋子位置:5
---
棋局编号:2
棋子类型:⭕
棋子位置:6
---
棋局编号:2
棋子类型:×
棋子位置:7
---
棋局编号:2
棋子类型:⭕
棋子位置:3
---
棋局编号:2
棋子类型:×
棋子位置:9
---
池大小:3
持⭕的玩家又输了!!
井字棋玩的少的人,可能看不出来,我再图形化展示一下
第一局:
第二局:
我们可以看到,池子里边放了一个棋盘、两个棋子对象后,就不再放新的对象,因为一直在复用。
个人理解:关于数据库连接池
相信大家学习享元模式,经常听到,什么什么池应用了享元模式,但是仔细一想,又不知道在哪里用了,一脸懵逼。下边就说一下个人理解。
先来理解一下池是什么。
生活中的小水池,池水资源可以供大家共同使用。
在咱们这个井字棋里边,有个pool的Map集合,里边放的棋盘、棋子资源,共调用者共同使用。其实,咱们的对象基本已经确定又3个了,不用集合也可以,提前声明好,需要用的时候就实例化。也可以。
所以说,咱们这个享元模式的案例中,有用池的思想。
现在应该理解池是什么了。
那么数据库连接池是怎们用享元模式呢?
分析一下数据库链接对象:每个链接的账号、密码、端口、地址等数据都是一样的。每次实例化链接对象时,都会把这些重复的信息再在内存里边生成。造成内存浪费。
现在,咱们把这些共同的链接信息单独写个类,实例化好。每次新建连接时,都来此对象取走信息进行连接。
相对来说,我们每次实例化连接时,占用的内存空间更少了。
重复使用数据库连接信息对象,这里使用了享元模式。
至于把所有的连接存到容器,动态生成或者销毁时属于池的思想了。
个人学习总结,如果有不对的地方,希望大佬指正