Java大话设计模式学习总结(二十六)---享元模式

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/a770794164/article/details/90734451

享元模式(Flyweight),运用共享技术有效地支持大量细粒度的对象。
来自大话设计模式
举例:
休闲开发游戏中,比如说围棋、五子棋、跳棋等,都有大量的棋子对象,如果用常规的面向对象方式编程,就会产生大量的对象,这样服务器就很难支持大规模量的玩家玩游戏了,因为内存有限。如果使用享元模式,那么棋子对象可以减少到2个实例,其中不同颜色的棋子就是两个不互相共享的享元类。

  1. 棋子抽象类,即享元类
abstract class Chess {
	abstract void buildChess();
}
  1. 享元实现类,两个互不干涉颜色的棋子
class BlackChess extends Chess {
	@Override
	void buildChess() {
		System.out.println("创建了一个黑色棋子");
	}
}

class WhiteChess extends Chess {
	@Override
	void buildChess() {
		System.out.println("创建了一个白色棋子");
	}
}
  1. 享元工厂类
class ChessFactory {
	
	private static HashMap<String, Chess> chessMap = new HashMap<>();
	
	// 获取各个棋子
	static Chess getChess(String color) {
		if (!chessMap.containsKey(color)) {
			Chess chess = null;
			if ("白色".equals(color)) {
				chess = new WhiteChess();
			} else {
				chess = new BlackChess();
			}
			chessMap.put(color, chess);
			return chess;
		} else {
			return chessMap.get(color);
		}
	}
	
	static int getChessCount() {
		return chessMap.size();
	}
}
  1. 主程序
class Test {

	public static void main(String[] args) {
		Chess white1 = ChessFactory.getChess("白色");
		Chess black1 = ChessFactory.getChess("黑色");
		Chess white2 = ChessFactory.getChess("白色");
		Chess black2 = ChessFactory.getChess("黑色");
		Chess white3 = ChessFactory.getChess("白色");
		Chess black3 = ChessFactory.getChess("黑色");
		
		white1.buildChess();
		white2.buildChess();
		white3.buildChess();
		System.out.println(white1 == white2);
		System.out.println(white2 == white3);
		
		black1.buildChess();
		black2.buildChess();
		black3.buildChess();
		System.out.println(black1 == black2);
		System.out.println(black2 == black3);
		
		System.out.println("棋子个数:" + ChessFactory.getChessCount());
	}

}
结果:
创建了一个白色棋子
创建了一个白色棋子
创建了一个白色棋子
true
true
创建了一个黑色棋子
创建了一个黑色棋子
创建了一个黑色棋子
true
true
棋子个数:2

可以看出来,虽然利用工厂创建了6个棋子对象,但实际对象只有2个。

内部状态与外部状态:
在享元对象内部并且不会随环境改变而改变的共享部分,可以称为是享元对象的内部状态,而随环境改变而改变的、不可以共享的状态就是外部状态。事实上,享元模式可以避免大量非常相似类的开销。在程序设计中,有时需要生成大量细粒度的类实例来标识数据。如果能发现这些实例除了几个参数外基本上都是相同的,有时就能够受大幅度地减少需要实例化的类的数量。如果能把那些参数移到类实例的外面,在方法调用时将他们传进来,就可以通过共享大幅度的减少单个实例的数目。

举例:
刚才的例子中,只有棋子的存在,这样实现多局游戏,就需要加入一个棋盘信息,表示是哪一局游戏,继续对代码进行调整:

  1. 添加棋盘类
class Chessboard {
	private String name;
	public Chessboard(String name) {
		this.name = name;
	}
	public String getName() {
		return name;
	}
}
  1. 调整享元类
abstract class Chess {
	abstract void buildChess(Chessboard chessboard);
}

class WhiteChess extends Chess {
	@Override
	void buildChess(Chessboard chessboard) {
		System.out.println("在棋盘:" + chessboard.getName() + " 创建了一个白色棋子");
	}
}

class BlackChess extends Chess {
	@Override
	void buildChess(Chessboard chessboard) {
		System.out.println("在棋盘:" + chessboard.getName() + " 创建了一个黑色棋子");
	}
}
  1. 主程序
class Test {

	public static void main(String[] args) {
		Chess white1 = ChessFactory.getChess("白色");
		Chess black1 = ChessFactory.getChess("黑色");
		Chess white2 = ChessFactory.getChess("白色");
		Chess black2 = ChessFactory.getChess("黑色");
		Chess white3 = ChessFactory.getChess("白色");
		Chess black3 = ChessFactory.getChess("黑色");
		
		white1.buildChess(new Chessboard("对局一"));
		white2.buildChess(new Chessboard("对局二"));
		white3.buildChess(new Chessboard("对局三"));
		System.out.println(white1 == white2);
		System.out.println(white2 == white3);
		
		black1.buildChess(new Chessboard("对局四"));
		black2.buildChess(new Chessboard("对局五"));
		black3.buildChess(new Chessboard("对局六"));
		System.out.println(black1 == black2);
		System.out.println(black2 == black3);
		
		System.out.println("棋子个数:" + ChessFactory.getChessCount());
	}

}
结果:
在棋盘:对局一 创建了一个白色棋子
在棋盘:对局二 创建了一个白色棋子
在棋盘:对局三 创建了一个白色棋子
true
true
在棋盘:对局四 创建了一个黑色棋子
在棋盘:对局五 创建了一个黑色棋子
在棋盘:对局六 创建了一个黑色棋子
true
true
棋子个数:2

使用场景

如果一个应用程序使用了大量的对象,而大量的这些对象造成了很大的存储开销时就应该考虑使用;还有就是对象的大多数状态可以外部状态,如果删除对象的外部状态,那么可以用相对较少的共享对象取代很多组对象,此时就可以考虑使用享元模式。 不过享元模式需要维护一个记录了系统已有的所有享元的列表,而这本身需要消耗资源,另外享元模式使得系统更加复杂。为了使对象可以共享,需要将一些状态外部化,这使得程序的逻辑复杂化。因此,应当在有足够多的对象实例可供享元时才值得使用享元模式。

猜你喜欢

转载自blog.csdn.net/a770794164/article/details/90734451