一、基礎(chǔ)簡(jiǎn)介
1、定義
運(yùn)用共享技術(shù)有效地支持大量細(xì)顆粒度的對(duì)象。主要用于減少創(chuàng)建對(duì)象的數(shù)量,以減少內(nèi)存占用和提高性能。這種類(lèi)型的設(shè)計(jì)模式屬于結(jié)構(gòu)型模式。
2、使用場(chǎng)景
- 1、系統(tǒng)中有大量對(duì)象。
- 2、這些對(duì)象消耗大量?jī)?nèi)存。
- 3、這些對(duì)象的狀態(tài)大部分可以外部化。
- 4、這些對(duì)象可以按照內(nèi)蘊(yùn)狀態(tài)分為很多組,當(dāng)把外蘊(yùn)對(duì)象從對(duì)象中剔除出來(lái)時(shí),每一組對(duì)象都可以用一個(gè)對(duì)象來(lái)代替。
- 5、系統(tǒng)不依賴(lài)于這些對(duì)象身份,這些對(duì)象是不可分辨的。
3、優(yōu)缺點(diǎn)
優(yōu)點(diǎn):
- 可以極大減少內(nèi)存中對(duì)象的數(shù)量,使得相同或相似對(duì)象在內(nèi)存中只保存一份,從而可以節(jié)約系統(tǒng)資源,提高系統(tǒng)性能。
- 享元模式的外部狀態(tài)相對(duì)獨(dú)立,而且不會(huì)影響其內(nèi)部狀態(tài),從而使得享元對(duì)象可以在不同的環(huán)境中被共享
缺點(diǎn):提高了系統(tǒng)的復(fù)雜度,需要分離出外部狀態(tài)和內(nèi)部狀態(tài),而且外部狀態(tài)具有固有化的性質(zhì),不應(yīng)該隨著內(nèi)部狀態(tài)的變化而變化,否則會(huì)造成系統(tǒng)的混亂。
4、模式結(jié)構(gòu)分析
- Flyweight(抽象享元類(lèi)):通常是一個(gè)接口或抽象類(lèi),在抽象享元類(lèi)中聲明了具體享元類(lèi)公共的方法,這些方法可以向外界提供享元對(duì)象的內(nèi)部數(shù)據(jù)(內(nèi)部狀態(tài)),同時(shí)也可以通過(guò)這些方法來(lái)設(shè)置外部數(shù)據(jù)(外部狀態(tài))。
- ConcreteFlyweight(具體享元類(lèi)):它實(shí)現(xiàn)了抽象享元類(lèi),其實(shí)例稱(chēng)為享元對(duì)象;在具體享元類(lèi)中為內(nèi)部狀態(tài)提供了存儲(chǔ)空間。通常我們可以結(jié)合單例模式來(lái)設(shè)計(jì)具體享元類(lèi),為每一個(gè)具體享元類(lèi)提供唯一的享元對(duì)象。
- UnsharedConcreteFlyweight(非共享具體享元類(lèi)):并不是所有的抽象享元類(lèi)的子類(lèi)都需要被共享,不能被共享的子類(lèi)可設(shè)計(jì)為非共享具體享元類(lèi);當(dāng)需要一個(gè)非共享具體享元類(lèi)的對(duì)象時(shí)可以直接通過(guò)實(shí)例化創(chuàng)建。
- FlyweightFactory(享元工廠類(lèi)):享元工廠類(lèi)用于創(chuàng)建并管理享元對(duì)象,它針對(duì)抽象享元類(lèi)編程,將各種類(lèi)型的具體享元對(duì)象存儲(chǔ)在一個(gè)享元池中,享元池一般設(shè)計(jì)為一個(gè)存儲(chǔ)“鍵值對(duì)”的集合(也可以是其他類(lèi)型的集合),可以結(jié)合工廠模式進(jìn)行設(shè)計(jì);當(dāng)用戶請(qǐng)求一個(gè)具體享元對(duì)象時(shí),享元工廠提供一個(gè)存儲(chǔ)在享元池中已創(chuàng)建的實(shí)例或者創(chuàng)建一個(gè)新的實(shí)例(如果不存在的話),返回新創(chuàng)建的實(shí)例并將其存儲(chǔ)在享元池中。
二、實(shí)例實(shí)現(xiàn)
1、實(shí)例場(chǎng)景
比如“五子棋”,有白色和黑色的棋子:
- 棋子:就是一個(gè) Flyweight(抽象享元類(lèi))
- 棋子的顏色:就屬于“內(nèi)部狀態(tài)”
- 棋子的位置:就屬于“外部狀態(tài)”
- 白棋和黑棋,就是ConcreteFlyweight(具體享元類(lèi))
- 而壞了的棋子,就是UnsharedConcreteFlyweight(非共享具體享元類(lèi))
- 五子棋加工廠,就是 FlyweightFactory(享元工廠類(lèi))
2、“五子棋”:Flyweight(抽象享元類(lèi))
package com.mfc.design.享元模式.實(shí)例;
/**
* @author MouFangCai
* @date 2019/10/25 14:29
*
* @description 五子棋棋子類(lèi):Flyweight(抽象享元類(lèi))
*/
public abstract class Chessman_Flyweight {
public abstract void display();
// 通過(guò)該接口,F(xiàn)lyweight可以接受并作用于外部狀態(tài)
public abstract void display(Coord_Extrinsic extrinsic);
}
3、外部狀態(tài)類(lèi)
package com.mfc.design.享元模式.實(shí)例;
import lombok.Data;
/**
* @author MouFangCai
* @date 2019/10/25 14:33
* @description 棋子的外部屬性:坐標(biāo)類(lèi)
*/
@Data
public class Coord_Extrinsic {
private int x;
private int y;
public Coord_Extrinsic(int x, int y) {
this.x = x;
this.y = y;
}
}
4、白棋 / 黑棋:ConcreteFlyweight(具體享元類(lèi))
package com.mfc.design.享元模式.實(shí)例;
/**
* @author MouFangCai
* @date 2019/10/25 14:48
*
* @description 具體的棋子:ConcreteFlyweight(具體享元類(lèi)):它實(shí)現(xiàn)了抽象享元類(lèi),其實(shí)例稱(chēng)為享元對(duì)象
*/
public class Chessman_ConcreteFlyweight extends Chessman_Flyweight{
// 為內(nèi)部狀態(tài)增加存儲(chǔ)空間
// 棋子的內(nèi)部屬性:顏色
private String color_intrinsic;
// 要求享元角色 必須接受內(nèi)部狀態(tài)
public Chessman_ConcreteFlyweight(String color_intrinsic) {
this.color_intrinsic = color_intrinsic;
}
@Override
public void display() {
System.out.println("棋子顏色:" + color_intrinsic + " 在棋盒里");
}
@Override
public void display(Coord_Extrinsic extrinsic) {
System.out.println("棋子顏色:" + color_intrinsic +
" 位置:(" + extrinsic.getX() + "," + extrinsic.getY() + ")");
}
}
5、壞了的棋子:UnsharedConcreteFlyweight(非共享具體享元類(lèi))
package com.mfc.design.享元模式.實(shí)例;
/**
* @author MouFangCai
* @date 2019/10/25 14:58
* @description UnsharedConcreteFlyweight(非共享具體享元類(lèi))
* 壞了的棋子就不管他的顏色了
*/
public class UnsharedConcreteFlyweight extends Chessman_Flyweight {
@Override
public void display() {
System.out.println("這個(gè)棋子已壞");
}
@Override
public void display(Coord_Extrinsic extrinsic) {
System.out.println("這個(gè)棋子已壞");
}
}
6、五子棋加工廠: FlyweightFactory(享元工廠類(lèi))
package com.mfc.design.享元模式.實(shí)例;
import java.util.HashMap;
/**
* @author MouFangCai
* @date 2019/10/25 15:01
* @description FlyweightFactory(享元工廠類(lèi)):享元工廠類(lèi)用于創(chuàng)建并管理享元對(duì)象
*/
public class Chessman_Factory {
// 使用HashMap存儲(chǔ)享元對(duì)象,充當(dāng)享元池
private static HashMap<String,Chessman_Flyweight> chessmanMaps;
// 使用單例模式,保證Chessman_Factory 類(lèi)只有一個(gè)實(shí)例
private static class FactoryClass {
private static final Chessman_Factory chessman = new Chessman_Factory();
}
// 初始化常用的棋子
private Chessman_Factory() {
chessmanMaps = new HashMap<>();
Chessman_Flyweight white = new Chessman_ConcreteFlyweight("白色");
chessmanMaps.put("w",white);
Chessman_Flyweight black = new Chessman_ConcreteFlyweight("黑色");
chessmanMaps.put("b",black);
Chessman_Flyweight unshared = new UnsharedConcreteFlyweight();
chessmanMaps.put("no",unshared);
}
// 得到 享元工廠類(lèi)的唯一實(shí)例
public static Chessman_Factory getFactory() {
return FactoryClass.chessman;
}
public Chessman_Flyweight getFlyweight(String color){
return chessmanMaps.get(color);
}
// 實(shí)例數(shù)
public int getCount(){
return chessmanMaps.size();
}
}
7、客戶端
package com.mfc.design.享元模式.實(shí)例;
/**
* @author MouFangCai
* @date 2019/10/25 15:01
* @description
*/
public class Client_Flyweight {
public static void main(String[] args) {
// 獲取享元工廠對(duì)象
Chessman_Factory factory = Chessman_Factory.getFactory();
// 獲取2枚黑棋、2枚白棋、2枚壞了的棋子
Chessman_Flyweight black1 = factory.getFlyweight("b");
Chessman_Flyweight black2 = factory.getFlyweight("b");
System.out.println("判斷黑棋是否相同:" + (black1 == black2));
Chessman_Flyweight white1 = factory.getFlyweight("w");
Chessman_Flyweight white2 = factory.getFlyweight("w");
System.out.println("判斷白棋是否相同:" + (white1 == white2));
Chessman_Flyweight bad1 = factory.getFlyweight("no");
Chessman_Flyweight bad2 = factory.getFlyweight("no");
System.out.println("判斷壞了的棋子是否相同:" + (bad1 == bad2));
System.out.println();
System.out.println("棋子的實(shí)例數(shù):" + factory.getCount());
System.out.println();
// 顯示棋子
black1.display();
white1.display();
bad1.display();
// 顯示棋子,同時(shí)設(shè)置坐標(biāo)位置
System.out.println();
black2.display(new Coord_Extrinsic(1,2));
white2.display(new Coord_Extrinsic(2,3));
bad2.display(new Coord_Extrinsic(1,2));
}
}
8、結(jié)果展示
判斷黑棋是否相同:true
判斷白棋是否相同:true
判斷壞了的棋子是否相同:true棋子的實(shí)例數(shù):3
棋子顏色:黑色 在棋盒里
棋子顏色:白色 在棋盒里
這個(gè)棋子已壞棋子顏色:黑色 位置:(1,2)
棋子顏色:白色 位置:(2,3)
這個(gè)棋子已壞Process finished with exit code 0
我們可以看到,棋子的實(shí)例數(shù)為3,但我們已有6顆棋子了。減少了創(chuàng)建對(duì)象的數(shù)量