一,什麼是享元模式
Flyweight 模式也叫享元模式,是構造模式之一,它通過與其他類似對象共享數據來減少內存佔用。
說到享元模式,第一個想到的應該就是池技術了,String常量池、數據庫連接池、緩衝池等等都是享元模式的應用,所以說享元模式是池技術的重要實現方式。
二,享元模式的結構
享元模式中存在以下兩種狀態:
- 內部狀態,即不會隨着環境的改變而改變的可共享部分;
- 外部狀態,指隨環境改變而改變的不可以共享的部分。享元模式的實現要領就是區分應用中的這兩種狀態,並將外部狀態外部化。下面來分析其基本結構和實現方法。
抽象享元角色 Flyweight): 所有具體享元類的父類,規定一些需要實現的公共接口。
具體享元角色 (Concrete Flyweight): 抽象享元的具體實現類,並實現了抽象享元角色規定的方法。
非享元(Unsharable Flyweight)角色:是不可以共享的外部狀態,它以參數的形式注入具體享元的相關方法中
享元工廠角色 (Flyweight Factory): 負責創建和管理享元角色。
優點:相同對象只要保存一份,這降低了系統中對象的數量,從而降低了系統中細粒度對象給內存帶來的壓力。
缺點:
- 爲了使對象可以共享,需要將一些不能共享的狀態外部化,這將增加程序的複雜性。
- 讀取享元模式的外部狀態會使得運行時間稍微變長。
四,簡化代碼實現:
4.1 抽象享元角色: 所有具體享元類的父類,規定一些需要實現的公共接口。
/**
* @ClassName Persion
* @Description TODO
* @Version 1.0
**/
public class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
/**
* @ClassName Flyweight
* @Description TODO
* @Version 1.0
**/
public interface Flyweight{
public void operation(UnsharedConcreteFlyweight state);
}
4.2 具體享元角色: 抽象享元的具體實現類,並實現了抽象享元角色規定的方法。
/**
* @ClassName Teacher
* @Description TODO
* @Version 1.0
**/
public class Teacher extends Person{
private String number;
public Teacher(String number) {
this.number = number;
}
public Teacher(String name, int age , String number) {
super(name, age);
this.number = number;
}
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
}
/**
* @ClassName ConcreteFlyweight
* @Description TODO
* @Version 1.0
**/
public ConcreteFlyweight implements Flyweight {
private String key;
public ConcreteFlyweight(String key) {
this.key=key;
System.out.println("具體享元"+key+"被創建!");
}
public void operation(UnsharedConcreteFlyweight outState) {
System.out.print("具體享元"+key+"被調用,");
System.out.println("非享元信息是:"+outState.getInfo());
}
}
4.3 非享元角色
/**
* @ClassName UnsharedConcreteFlyweight
* @Description 非享元角色
* @Version 1.0
**/
public class UnsharedConcreteFlyweight {
private String info;
public UnsharedConcreteFlyweight(String info) {
this.info=info;
}
public String getInfo() {
return info;
}
public void setInfo(String info) {
this.info=info;
}
}
4.4 享元工廠角色: 負責創建和管理享元角色。
public class TeacherFactory {
Map<String , Teacher> pool ;
public TeacherFactory(Map<String, Teacher> pool) {
this.pool = pool;
}
public Teacher getTeacher(String number){
Teacher teacher = pool.get(number);
if(teacher == null ){
teacher = new Teacher("1");
pool.put(number,teacher);
}
return teacher;
}
}
/**
* @ClassName FlyweightFactory
* @Description TODO
* @Version 1.0
**/
public class FlyweightFactory{
private HashMap<String, Flyweight> flyweights=new HashMap<String, Flyweight>();
public Flyweight getFlyweight(String key) {
Flyweight flyweight=(Flyweight)flyweights.get(key);
if(flyweight!=null) {
System.out.println("具體享元"+key+"已經存在,被成功獲取!");
}else {
flyweight=new ConcreteFlyweight(key);
flyweights.put(key, flyweight);
}
return flyweight;
}
}
測試:
public class Test {
public static void main(String[] args) {
Map<String , Teacher> pool =new HashMap<String, Teacher> ();
TeacherFactory teacherFactory = new TeacherFactory(pool);
Teacher teacher1 = teacherFactory.getTeacher("1");
Teacher teacher2 = teacherFactory.getTeacher("2");
Teacher teacher3 = teacherFactory.getTeacher("1");
Teacher teacher4 = teacherFactory.getTeacher("2");
if(teacher1 == teacher3){
System.out.println( "teacher1 和 teacher3 是同一個對象");
}
if(teacher2 == teacher4){
System.out.println( "teacher2 和 teacher4 是同一個對象");
}
}
}
/**
* @ClassName FlyweightPattern
* @Description 測試
* @Version 1.0
**/
public class FlyweightPattern {
public static void main(String[] args) {
FlyweightFactory factory=new FlyweightFactory();
Flyweight f01=factory.getFlyweight("a");
Flyweight f02=factory.getFlyweight("a");
Flyweight f03=factory.getFlyweight("a");
Flyweight f11=factory.getFlyweight("b");
Flyweight f12=factory.getFlyweight("b");
f01.operation(new UnsharedConcreteFlyweight("第1次調用a。"));
f02.operation(new UnsharedConcreteFlyweight("第2次調用a。"));
f03.operation(new UnsharedConcreteFlyweight("第3次調用a。"));
f11.operation(new UnsharedConcreteFlyweight("第1次調用b。"));
f12.operation(new UnsharedConcreteFlyweight("第2次調用b。"));
}
}
五:享元模式的應用實例
【例1】享元模式在五子棋遊戲中的應用。
分析:五子棋同圍棋一樣,包含多個“黑”或“白”顏色的棋子,所以用享元模式比較好。
本實例中的棋子
(ChessPieces)類是抽象享元角色,它包含了一個落子的 DownPieces(Graphics g,Point pt) 方法;
白子(WhitePieces)和黑子(BlackPieces)類是具體享元角色,它實現了落子方法;
Point 是非享元角色,它指定了落子的位置;
WeiqiFactory 是享元工廠角色,它通過 ArrayList 來管理棋子,並且提供了獲取白子或者黑子的 getChessPieces(String type) 方法;客戶類(Chessboard)利用 Graphics 組件在框架窗體中繪製一個棋盤,並實現 mouseClicked(MouseEvent e) 事件處理方法,該方法根據用戶的選擇從享元工廠中獲取白子或者黑子並落在棋盤上。圖 2 所示是其結構圖。
抽象享元角色:棋子
/**
* @ClassName BlackPieces
* @Description 抽象享元角色:棋子
* @Version 1.0
**/
public interface ChessPieces {
/**
* @Description: 下子
* @param: [g, pt]
* @return: void
* @Author: wuchao
* @Date: 2020/6/18 23:26
**/
public void DownPieces(Graphics g, Point pt);
}
具體享元角色:黑子
/**
* @ClassName BlackPieces
* @Description 具體享元角色:黑子
* @Version 1.0
**/
public class BlackPieces implements ChessPieces {
@Override
public void DownPieces(Graphics g, Point pt) {
g.setColor(Color.BLACK);
g.fillOval(pt.x,pt.y,30,30);
}
}
具體享元角色:白子
/**
* @ClassName WhitePieces
* @Description 具體享元角色:白子
* @Version 1.0
**/
public class WhitePieces implements ChessPieces {
@Override
public void DownPieces(Graphics g, Point pt) {
g.setColor(Color.WHITE);
g.fillOval(pt.x,pt.y,30,30);
}
}
棋盤:
/**
* @ClassName Chessboard
* @Description 棋盤
* @Version 1.0
**/
public class Chessboard extends MouseAdapter {
WeiqiFactory wf;
JFrame f;
Graphics g;
JRadioButton wz;
JRadioButton bz;
private final int x=50;
private final int y=50;
private final int w=40; //小方格寬度和高度
private final int rw=400; //棋盤寬度和高度
public Chessboard(){
wf=new WeiqiFactory();
f=new JFrame("享元模式在五子棋遊戲中的應用");
f.setBounds(100,100,500,550);
f.setVisible(true);
f.setResizable(false);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel SouthJP=new JPanel();
f.add("South",SouthJP);
wz=new JRadioButton("白子");
bz=new JRadioButton("黑子",true);
ButtonGroup group=new ButtonGroup();
group.add(wz);
group.add(bz);
SouthJP.add(wz);
SouthJP.add(bz);
JPanel CenterJP=new JPanel();
CenterJP.setLayout(null);
CenterJP.setSize(500, 500);
CenterJP.addMouseListener(this);
f.add("Center",CenterJP);
try
{
Thread.sleep(500);
}
catch(InterruptedException e)
{
e.printStackTrace();
}
g=CenterJP.getGraphics();
g.setColor(Color.BLUE);
g.drawRect(x, y, rw, rw);
for(int i=1;i<10;i++)
{
//繪製第i條豎直線
g.drawLine(x+(i*w),y,x+(i*w),y+rw);
//繪製第i條水平線
g.drawLine(x,y+(i*w),x+rw,y+(i*w));
}
};
@Override
public void mouseClicked(MouseEvent e)
{
Point pt=new Point(e.getX()-15,e.getY()-15);
if(wz.isSelected())
{
ChessPieces c1=wf.getChessPieces("w");
c1.DownPieces(g,pt);
}
else if(bz.isSelected())
{
ChessPieces c2=wf.getChessPieces("b");
c2.DownPieces(g,pt);
}
}
}
享元工廠角色
/**
* @ClassName WeiqiFactory
* @Description 享元工廠角色
* @Version 1.0
**/
public class WeiqiFactory {
private ArrayList<ChessPieces> qz;
public WeiqiFactory() {
qz=new ArrayList<ChessPieces>();
ChessPieces w=new WhitePieces();
qz.add(w);
ChessPieces b=new BlackPieces();
qz.add(b);
}
public ChessPieces getChessPieces(String type) {
if(type.equalsIgnoreCase("w")) {
return (ChessPieces)qz.get(0);
} else if(type.equalsIgnoreCase("b")) {
return (ChessPieces)qz.get(1);
} else {
return null;
}
}
}
測試:
public class WzqGame {
public static void main(String[] args) {
new Chessboard();
}
}
六:享元模式的擴展
其結構圖通常包含可以共享的部分和不可以共享的部分。在實際使用過程中,有時候會稍加改變,即存在兩種特殊的享元模式:單純享元模式和複合享元模式,下面分別對它們進行簡單介紹。
1.單純享元模式,這種享元模式中的所有的具體享元類都是可以共享的,不存在非共享的具體享元類,
2.複合享元模式,這種享元模式中的有些享元對象是由一些單純享元對象組合而成的,它們就是複合享元對象。雖然複合享元對象本身不能共享,但它們可以分解成單純享元對象再被共享。