目录
什么是享元模式
享元模式的实现
享元模式的特点
什么是享元模式
享元模式(Flyweight Pattern)是一种结构型设计模式,享元模式中的“享元”指被共享的单元,享元模式通过复用对象,以达到节省内存的目的。要求能够共享的对象必须是细粒度对象,因此它又称为轻量级模式。其主要解决的问题是创建大量相似对象时的内存开销过大。
其意图运用共享技术有效地支持大量细粒度的对象。解决在有大量对象时,有可能会造成内存溢出问题,把其中共同的部分抽象出来,查找时直接返回在内存中已有的对象,没有才重新创建,避免重新创建已有对象。减少创建对象的数量,以减少内存占用和提高性能,避免大量相似对象的开销,从而提高系统资源的利用率,尽可能复用现有的同类对象。
“享元”,被共享的单元,即复用对象,节省内存,注意前提是享元对象是不可变对象。
当一个系统中存在大量重复不可变对象,就能利用享元模式将对象设计成享元,在内存中只保留一份实例,供引用。这就减少内存中对象的数量,最终节省内存。当然,不仅相同对象可设计成享元,相似对象,也能提取对象中的相同部分(字段)设计成享元。
“不可变对象”:一旦通过构造器初始化完成后,其状态(对象的成员变量或属性)就不会再被修改。所以,不可变对象不能暴露任何set()等修改内部状态的方法。之所以要求享元是不可变对象,是因为它会被多处代码共享使用,避免一处代码对享元进行了修改,影响到其他使用它的代码。
享元模式的实现
享元(Flyweight )模式中存在以下两种状态:
1. 内部状态,即不会随着环境的改变而改变的可共享部分。
2. 外部状态,指随环境改变而改变的不可以共享的部分。享元模式的实现要领就是区分应用中的这两种状态,并将外部状态外部化。
享元模式的主要有以下角色:
抽象享元角色(Flyweight):通常是一个接口或抽象类,在抽象享元类中声明了具体享元类公 共的方法,这些方法可以向外界提供享元对象的内部数据(内部状态),同时也可以通过这些方法来设置外部数据(外部状态)。
具体享元角色(Concrete Flyweight):实现了抽象享元类,称为享元对象;在具体享元 类中为内部状态提供了存储空间。通常我们可以结合单例模式来设计具体享元类,为每一个具体享元类提供唯一的享元对象。
非享元角色(Unsharable Flyweight) :并非所有的抽象享元类的子类都需要被共享,不 能被共享的子类可设计为非共享具体享元类;当需要一个非共享具体享元类的对象时可以直接通过实例化创建。
享元工厂角色(Flyweight Factory):负责创建和管理享元角色。当客户对象请求一个享元 对象时,享元工厂检査系统中是否存在符合要求的享元对象,存在则提供给客户,不存在新创建提供给客户。
享元模式类图:
享元模式代码实现:
抽象享元角色
package com.common.demo.pattern.flyweight;
/**
* @author Evan Walker 昂焱数据: https://www.ayshuju.com
* @version 1.0
* @desc 抽象享元角色 篮球
* @date 2023/07/21 15:03:57
*/
public interface BasketBall {
void play();
}
具体享元角色和非享元角色
package com.common.demo.pattern.flyweight;
/**
* @author Evan Walker 昂焱数据: https://www.ayshuju.com
* @version 1.0
* @desc 具体享元角色 体育馆
* @date 2023/07/21 15:05:37
*/
public class Gymnasium implements BasketBall{
private String gymnasiumName;
private String sport = "篮球";
double price = 50;
public Gymnasium() {
}
public Gymnasium(String gymnasiumName) {
this.gymnasiumName = gymnasiumName;
}
public String getGymnasiumName() {
return gymnasiumName;
}
public void setGymnasiumName(String gymnasiumName) {
this.gymnasiumName = gymnasiumName;
}
public String getSport() {
return sport;
}
public void setSport(String sport) {
this.sport = sport;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
@Override
public void play() {
System.out.println("Gymnasium{" + "gymnasiumName='" + gymnasiumName + '\'' +
", sport='" + sport + '\'' + ", price=" + price + '}'
+", this Object='" + this + '\'' );
}
}
享元工厂角色
package com.common.demo.pattern.flyweight;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
/**
* @author Evan Walker 昂焱数据: https://www.ayshuju.com
* @version 1.0
* @desc 享元工厂 篮球工厂
* @date 2023/07/21 15:11:37
*/
public class BasketBallFactory {
private static Map<String,Gymnasium> MAP = new HashMap<>();
public static Gymnasium getBasketBall(String gymnasiumName){
Gymnasium gymnasium = MAP.get(gymnasiumName);
if(Objects.isNull(gymnasium)){
gymnasium = new Gymnasium(gymnasiumName);
MAP.put(gymnasiumName,gymnasium);
}
return gymnasium;
}
}
测试类
package com.common.demo.pattern.flyweight;
import java.util.ArrayList;
import java.util.List;
/**
* @author Evan Walker 昂焱数据: https://www.ayshuju.com
* @version 1.0
* @desc
* @date 2023/07/21 15:16:00
*/
public class Test {
private static final List<String> nameList = new ArrayList<>();
public static void main(String[] args) {
nameList.add("斯塔普斯中心球馆");
nameList.add("大通中心球馆");
nameList.add("麦迪逊花园球馆");
nameList.add("联合中心球馆");
nameList.add("速贷中心球馆");
nameList.add("斯塔普斯中心球馆");
nameList.add("速贷中心球馆");
nameList.add("美航球馆");
nameList.add("丹佛百事中心球馆");
for (String name : nameList){
BasketBallFactory.getBasketBall(name).play();
}
}
}
测试截图
享元模式的特点
优点:
- 大大减少对象的创建,对象复用,降低系统的内存,使效率提高。
缺点:
- 提高了系统的复杂度,需要分离出外部状态和内部状态,而且外部状态具有固有化的性质,不应该随着内部状态的变化而变化,否则会造成线程安全问题。
- 需要牺牲一定的时间和空间,来实现对象共享和控制机制。当对象之间没有复用性时,使用享元模式可能会导致额外的开销。
使用场景:
- 系统有大量相似对象。
- 当对象需要被共享时,如需要缓冲池的场景。
- 当系统的内存资源相对有限时可以考虑使用享元模式,以减少内存的使用。
- 当需要减少对象的创建次数、降低系统开销时。
注意事项:
- 注意划分外部状态和内部状态,否则可能会引起线程安全问题。
- 这些类必须有一个工厂对象加以控制。
更多消息资讯,请访问昂焱数据(https://www.ayshuju.com)