主页传送门:💁 传送
1.享元模式定义
享元模式(Flyweight Pattern)是池技术的重要实现方式,是构造型模式之一,它通过与其他类似对象共享数据来减小内存占用。其定义如下:
Use sharing to support large numbers of fine-grained objects efficiently.
即:使用共享对象可有效地支持大量的细粒度的对象。
其通用类图如下:
2.享元模式的角色
享元模式所涉及到的角色有:
-
抽象享元类(Flyweight):
它是所有具体享元类的超类。为这些类规定出需要实现的公共接口,那些需要外蕴状态(Exte)的操作可以通过方法的参数传入。抽象享元的接口使得享元变得可能,但是并不强制子类实行共享,因此并非所有的享元对象都是可以共享的。 -
具体享元类(Concrete Flyweight):
具体享元类实现了抽象享元类所规定的接口。如果有内蕴状态的话,必须负责为内蕴状态提供存储空间。享元对象的内蕴状态必须与对象所处的周围环境无关,从而使得享元对象可以在系统内共享。有时候具体享元类又称为单纯具体享元类,因为复合享元类是由单纯具体享元角色通过复合而成的。 -
不能共享的具体享元类(Unsharable Flyweight):
不能共享的享元类,又叫做复合享元类。一个复合享元对象是由多个单享元对象组成,这些组成的对象是可以共享的,但是复合享元类本身并不能共享。 -
享元工厂类(Flyweight Factoiy):
享元工厂类负责创建和管理享元对象。当一个客户端对象请求一个享元对象的时候,享元工厂需要检查系统中是否已经有一个符合要求的享元对象,如果已经有了,享元工厂角色就应当提供这个已有的享元对象;如果系统中没有适当的享元对象的话,享元工厂角色就应当创建一个新的合适的享元对象。
3.享元模式实战案例
3.1.场景说明
本次采用图书馆借书的案例来理解享元模式,形式很简单,首先定义一个抽象类,里面有个抽象方法,接着定义子类来继承抽象类,并实现其方法。为了模拟外部状态,还需定义一个用户实体类。构建工厂来作为池存放对象,并暴露获取对象的方法。最后通过调用方法。
3.2.结构类图
使用享元模式来实现的结构图如下所示::
3.3.代码实现
抽象类的定义
只有一个借书的方法
package com.lyd.demo.flyweight;
import com.lyd.demo.entity.User;
public abstract class Library {
public abstract void borrow(User user); // 抽象方法 - 借书
}
抽象类的子类
实现抽象方法
package com.lyd.demo.flyweight;
import com.lyd.demo.entity.User;
public class LibraryImpl extends Library {
private String name; // 书名 - 内部状态
public LibraryImpl(String name) {
this.name = name;
}
@Override
public void borrow(User user) {
System.out.println("图书馆的书《" + name + "》已被[" + user.getName() + "]借出");
}
}
外部状态-User实体类
package com.lyd.demo.entity;
public class User {
private String name;
public User(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
工厂类
用Map集合来充当缓冲池,存放对象,并且提供获取对象的方法,为了方便观察享元模式的作用,还有个获取对象池中对象数量。
package com.lyd.demo.factory;
import com.lyd.demo.flyweight.Library;
import com.lyd.demo.flyweight.LibraryImpl;
import java.util.HashMap;
public class LibraryFactory {
// 用集合来充当缓存池,暂存书籍
private HashMap<String, LibraryImpl> pool = new HashMap<String, LibraryImpl>();
// 如果有书籍就放入缓存
public Library getLibraryImpl(String name) {
if (!pool.containsKey(name)) {
// 创建一个放入
pool.put(name, new LibraryImpl(name));
}
return pool.get(name);
}
// 获取书籍个数
public int bookCount() {
return pool.size();
}
}
测试
通过工厂类调用获得对象,再去调用借书方法,采用两种不用角色和不同数据来演绎享元模式的案例实现。
package com.lyd.demo.test;
import com.lyd.demo.entity.User;
import com.lyd.demo.factory.LibraryFactory;
import com.lyd.demo.flyweight.Library;
public class FlyWeightTest {
public static void main(String[] args) {
// 创建工厂
LibraryFactory libraryFactory = new LibraryFactory();
Library book = libraryFactory.getLibraryImpl("Java设计模式");
book.borrow(new User("怒放吧德德"));
// 假设书已经归还
Library book2 = libraryFactory.getLibraryImpl("Java设计模式");
book2.borrow(new User("愤怒吧德德"));
Library book3 = libraryFactory.getLibraryImpl("Go语言编程");
book3.borrow(new User("怒放吧德德"));
System.out.println("现在有书:" + libraryFactory.bookCount() + " 本");
}
}
运行结果:
4.享元模式优缺点
享元模式的主要优点和缺点如下:
优点:
- 减少内存占用:享元模式通过共享相同或相似的对象,可以在内存中只保存一份,从而节约系统资源,提高系统性能。
- 提高代码复用性:享元模式使得相同的对象可以被多个客户端共享使用,提高了代码的复用性。
缺点:
- 提高了系统的复杂性:享元模式需要分离出内部状态和外部状态,而且外部状态具有固化性,不应该随着内部状态的改变而改变,这使得程序逻辑复杂化。
- 需要维护一个存储享元对象的享元池:为了使对象可以共享,享元模式需要将享元对象的部分状态外部化,而读取外部状态将使得运行时间变长。
总的来说,享元模式适用于有大量相同或相似对象的系统,通过共享对象可以减少内存占用和提高代码复用性。但需要注意其增加系统复杂性和需要维护享元池的缺点。
5.享元模式适用场景
享元模式适用于以下场景:
- 系统有大量相似对象,需要缓冲池的场景。比如游戏中的地图元素、图形绘制中大量相似图形等。
- 对于文本编辑器、网页渲染等需要处理大量重复内容的场景,可以使用享元模式提高性能。
在这些场景下,享元模式通过共享对象,可以减少内存占用和提高性能。
6.享元模式总结
享元模式是一种用于优化内存占用和提高性能的设计模式。它通过共享相同或相似的对象,减少内存占用,提高系统的性能和代码的复用性。在使用享元模式时,需要注意分离出内部状态和外部状态,维护一个存储享元对象的享元池,并处理好对象的共享和独立状态。享元模式适用于有大量相同或相似对象的系统,可以提高系统的性能和可扩展性。
如果喜欢的话,欢迎 🤞关注 👍点赞 💬评论 🤝收藏 🙌一起讨论
你的支持就是我✍️创作的动力! 💞💞💞