一、介绍:
1、定义:享元模式(Flyweight Pattern)主要用于减少创建对象的数量,以减少内存占用和提高性能。这种类型的设计模式属于结构型模式,它提供了减少对象数量从而改善应用所需的对象结构的方式。
2、组成结构:
(1)Flyweight(抽象享元类):通常是一个接口或抽象类,在抽象享元类中声明了具体享元类公共的方法,这些方法可以向外界提供享元对象的内部数据(内部状态),同时也可以通过这些方法来设置外部数据(外部状态)。
public abstract class Flyweight{
public abstract void operation(String extrinsicState);
}
(2)ConcreteFlyweight(具体享元类):它实现了抽象享元类,其实例称为享元对象;在具体享元类中为内部状态提供了存储空间。通常我们可以结合单例模式来设计具体享元类,为每一个具体享元类提供唯一的享元对象。
public class ConcreteFlyweight extends Flyweight{
//内部状态intrinsicState作为成员变量,同一个享元对象的内部状态是一致的
private String intrinsicState;
public ConcreteFlyweight(String intrinsicState){
this.intrinsicState = intrinsicState;
}
//外部状态extrinsicState在使用时由外部设置,不保存在享元对象中,即使是同一个对象
//在每一次调用时可以传入不同的外部状态
public void operation(String extrinsicState){
//实现业务方法
}
}
(3)UnsharedConcreteFlyweight(非共享具体享元类):并不是所有的抽象享元类的子类都需要被共享,不能被共享的子类可设计为非共享具体享元类;当需要一个非共享具体享元类的对象时可以直接通过实例化创建。
public class UnsharedConcreteFlyweight extends Flyweight{
public void operation(String extrinsicState){
//实现业务方法
}
}
(4)FlyweightFactory(享元工厂类):享元工厂类用于创建并管理享元对象,它针对抽象享元类编程,将各种类型的具体享元对象存储在一个享元池中,享元池一般设计为一个存储“键值对”的集合(也可以是其他类型的集合),可以结合工厂模式进行设计;当用户请求一个具体享元对象时,享元工厂提供一个存储在享元池中已创建的实例或者创建一个新的实例(如果不存在的话),返回新创建的实例并将其存储在享元池中。
public class FlyweightFactory{
//定义一个HashMap用于存储享元对象,实现享元池
private HashMap flyweights = new HashMap();
public Flyweight getFlyweight(String key){
//如果对象存在,则直接从享元池获取
if(flyweight.containsKey(key)){
return (Flyweight)flyweight.get(key);
}
//如果对象不存在,先创建一个新的对象添加到享元池中,然后返回
else{
Flyweight fw = new ConcreteFlyweight();
flyweights.put(key,fw);
return fw;
}
}
}
3、享元模式将对象的状态分为内部状态和外部状态:
(1)内部状态:是对象可共享出来的信息,存储在享元对象内部并且不会随环境改变而改变,如用户的ID、Name,它们可以作为一个对象的动态附加信息,不必直接储存在具体某个对象中,属于可以共享的部分。即在生成对象后,类的属性每个对象都不一样,那他就是内部状态,用户的ID是每个用户都不一样的。
(2)外部状态:是对象得以依赖的一个标记,是随环境改变而改变的、不可以共享的状态,它是一批对象的统一标识,是唯一的一个索引值。即在生成对象后,类的属性大部分对象或部分的值相同,那么这个属性就是外部状态,例如用户属于VIP用户,很多用户都属于VIP用户。
4、优缺点:
优点:
(1)减少对象的创建,降低内存中对象的数量,降低系统的内存,提高效率
(2)减少内存之外的其他资源占用 (减少new 关键字的创建次数)
缺点:
(1)关注内/外部状态、关注线程安全问题
(2)使系统、程序的逻辑复杂化
5、相关设计模式:
(1)代理模式:代理模式是需要代理一个类,需要生成的代理类花费的资源和时间比较多就可以使用享元模式提高;
(2)单例模式:容器单例模式就是享元模式和单例模式的结合,使用复用的思想提高程序的使用频率。
6、使用场景:
二、demo:
1、黑白棋:黑白棋游戏需要大量的棋子对象,如果为每一个棋子都单独创建一个对象,系统的性能和内存开销都非常大。使用享元模式,可以将一些相同的棋子对象共享起来,只需要保存其内部和外部状态区分即可。文章来源:https://www.toymoban.com/news/detail-733969.html
/**
* 抽象的棋子接口
*/
public interface Piece {
void put(int x, int y);
}
/**
* 黑棋类
*/
public class BlackPiece implements Piece {
@Override
public void put(int x, int y) {
System.out.println("在坐标 (" + x + ", " + y + ") 放置了一个黑棋子");
}
}
/**
* 白棋类
*/
public class WhitePiece implements Piece {
@Override
public void put(int x, int y) {
System.out.println("在坐标 (" + x + ", " + y + ") 放置了一个白棋子");
}
}
//棋子享元工厂类
public class PieceFactory {
private final Map<String, Piece> map = new HashMap<>();
public Piece getPiece(String color) {
Piece piece = map.get(color);
if (piece == null) {
switch (color) {
case "black":
piece = new BlackPiece();
break;
case "white":
piece = new WhitePiece();
break;
default:
throw new IllegalArgumentException("Unsupported color: " + color);
}
map.put(color, piece);
}
return piece;
}
}
//客户端
public static void main(String[] args) {
PieceFactory factory = new PieceFactory();
Piece black1 = factory.getPiece("black");
black1.put(1, 1);
Piece black2 = factory.getPiece("black");
black2.put(2, 2);
Piece white1 = factory.getPiece("white");
white1.put(3, 3);
Piece white2 = factory.getPiece("white");
white2.put(4, 4);
}
输出:
在坐标 (1, 1) 放置了一个黑棋子
在坐标 (2, 2) 放置了一个黑棋子
在坐标 (3, 3) 放置了一个白棋子
在坐标 (4, 4) 放置了一个白棋子
2、扑克牌游戏:54张扑克牌(1到13四种花色)+大王+小王:文章来源地址https://www.toymoban.com/news/detail-733969.html
到了这里,关于设计模式(22)享元模式的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!