什麼是享元模式(Flyweight)?我用Java模擬玩井字棋來告訴你!(Java版)

定義

享元模式(Flyeight):運用共享技術有效的支持大量細顆粒度的對象。

簡單來說就是,共享對象、重複利用對象。

另外,享元模式還有四個角色、兩個狀態。

四個角色:

  1. 抽象享元角色(Flyweight):是所有的具體享元類的基類,爲具體享元規範需要實現的公共接口,非享元的外部狀態以參數的形式通過方法傳入。
  2. 具體享元(Concrete Flyweight)角色:實現抽象享元角色中所規定的接口。
  3. 非享元(Unsharable Flyweight)角色:是不可以共享的外部狀態,它以參數的形式注入具體享元的相關方法中。
  4. 享元工廠(Flyweight Factory)角色:負責創建和管理享元角色。當客戶對象請求一個享元對象時,享元工廠檢査系統中是否存在符合要求的享元對象,如果存在則提供給客戶;如果不存在的話,則創建一個新的享元對象。

兩個狀態:

  1. 內部狀態:即不會隨着環境的改變而改變的可共享部分。比如棋子的類型,不會隨着環境改變而改變。
  2. 外部狀態,指隨環境改變而改變的不可以共享的部分。比如棋子的位置,隨着人下的位置而改變。

享元模式的UML類圖:
享元模式的UML類圖

Java模式井字棋遊戲

分析

需求明確了,咱們來分析一波。

下棋需要棋盤和棋子,這些都是井字棋的東西。——井字棋Tictactoe可作爲接口或者抽象類
棋盤可以顯示自己可下部分的編號——棋盤Chessboard需要有二維數組作爲棋盤,並且作爲共享部分
棋子類型有兩種×、⭕(我輸入的是樸實無華的圓圈,但是CSDN直接給變成這樣了),兩外棋子有自己的位置——棋子的類型(ChessPieces)是內部狀態,棋子的位置是外部狀態(ChessPiecesSite)需要分開寫

再寫一個工廠類作爲池,負責給客戶端提供棋盤和棋子

UML類圖

井字棋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個了,不用集合也可以,提前聲明好,需要用的時候就實例化。也可以。
所以說,咱們這個享元模式的案例中,有用池的思想。

現在應該理解池是什麼了。

那麼數據庫連接池是怎們用享元模式呢?

分析一下數據庫鏈接對象:每個鏈接的賬號、密碼、端口、地址等數據都是一樣的。每次實例化鏈接對象時,都會把這些重複的信息再在內存裏邊生成。造成內存浪費。

現在,咱們把這些共同的鏈接信息單獨寫個類,實例化好。每次新建連接時,都來此對象取走信息進行連接。

相對來說,我們每次實例化連接時,佔用的內存空間更少了。

重複使用數據庫連接信息對象,這裏使用了享元模式。

至於把所有的連接存到容器,動態生成或者銷燬時屬於池的思想了。

個人學習總結,如果有不對的地方,希望大佬指正

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章