Java设计模式之享元模式
作者:董秀才 发布时间:2022-09-23 12:16:07
本文介绍了Java设计模式之享元模式,供大家参考,具体内容如下
1、关于享元模式
享元模式有点类似于单例模式,都是只生成一个对象被共享使用。享元模式主要目的就是让多个对象实现共享,减少不会要额内存消耗,将多个对同一对象的访问集中起来,不必为每个访问者创建一个单独的对象,以此来降低内存的消耗。
2、享元模式结构图
因为享元模式结构比较复杂,一般结合工厂模式一起使用,在它的结构图中包含了一个享元工厂类。
在享元模式结构图中包含如下几个角色:
Flyweight(抽象享元类):通常是一个接口或抽象类,在抽象享元类中声明了具体享元类公共的方法,这些方法可以向外界提供享元对象的内部数据(内部状态),同时也可以通过这些方法来设置外部数据(外部状态)。
ConcreteFlyweight(具体享元类):它实现了抽象享元类,其实例称为享元对象;在具体享元类中为内部状态提供了存储空间。通常我们可以结合单例模式来设计具体享元类,为每一个具体享元类提供唯一的享元对象。
UnsharedConcreteFlyweight(非共享具体享元类):并不是所有的抽象享元类的子类都需要被共享,不能被共享的子类可设计为非共享具体享元类;当需要一个非共享具体享元类的对象时可以直接通过实例化创建。
FlyweightFactory(享元工厂类):享元工厂类用于创建并管理享元对象,它针对抽象享元类编程,将各种类型的具体享元对象存储在一个享元池中,享元池一般设计为一个存储“键值对”的集合(也可以是其他类型的集合),可以结合工厂模式进行设计;当用户请求一个具体享元对象时,享元工厂提供一个存储在享元池中已创建的实例或者创建一个新的实例(如果不存在的话),返回新创建的实例并将其存储在享元池中。
3、享元模式的实现
在享元模式中引入了享元工厂类,享元工厂类的作用在于提供一个用于存储享元对象的享元池,当用户需要对象时,首先从享元池中获取,如果享元池中不存在,则创建一个新的享元对象返回给用户,并在享元池中保存该新增对象。
接下来,实现一个登陆的享元模式。
1、用户类
/**
* 用户类
* @author 董秀才
*
*/
public class User {
private String username; // 用户名
private String password; // 密码
public User(String username,String password) {
this.username = username;
this.password = password;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
2、抽象的登陆者(抽象享元类)
/**
* 登陆者--抽象享元类
* @author 董秀才
*
*/
public abstract class Loginer {
//登陆--享元类公共方法
public abstract void login(User user);
}
3、具体的登陆者(具体享元类)
/**
* 具体享元类
* @author 董秀才
*
*/
public class ConcreteLoginer extends Loginer{
// 登陆者凭证
private String loginerKey = "";
public ConcreteLoginer(String loginerKey) {
this.loginerKey = loginerKey;
}
@Override
public void login(User user) {
System.out.println("登陆者凭证:" + this.loginerKey+",用户名:" + user.getUsername() + ",密码:" + user.getPassword());
}
}
4、具体登陆者的工厂类(享元工厂类)
/**
* 享元工厂类
* @author 董秀才
*
*/
public class ConcreteLoginerFactory {
// map充当对象享元池
private static Map<String,ConcreteLoginer> loginerMap = new HashMap<String, ConcreteLoginer>();
public static ConcreteLoginer getConcreteLoginer(String key) {
// 从享元池中拿 登陆者对象
ConcreteLoginer concreteLoginer = loginerMap.get(key);
// 如果享元池中没有此对象
if(concreteLoginer == null) {
// 创建对象
concreteLoginer = new ConcreteLoginer(key);
// 存到享元池中
loginerMap.put(key, concreteLoginer);
}
// 返回对象
return concreteLoginer;
}
// 返回享元池对象数量
public static int getSize() {
return loginerMap.size();
}
}
5、测试类
/**
* 博客测试类
* @author 董秀才
*
*/
public class MainTest {
public static void main(String[] args) {
// 去工厂拿对象
ConcreteLoginer concreteLoginer_1 = ConcreteLoginerFactory.getConcreteLoginer("csdn");
concreteLoginer_1.login(new User("董秀才","123456"));
ConcreteLoginer concreteLoginer_2 = ConcreteLoginerFactory.getConcreteLoginer("csdn");
concreteLoginer_2.login(new User("董秀才","123456"));
ConcreteLoginer concreteLoginer_3 = ConcreteLoginerFactory.getConcreteLoginer("csdn");
concreteLoginer_3.login(new User("董秀才","123456"));
// 测试是否是同一个对象
System.out.println("是否是同一个对象:" + ((concreteLoginer_1==concreteLoginer_2)&&(concreteLoginer_2 == concreteLoginer_3)));
// 第二登陆者
ConcreteLoginer concreteLoginer_4 = ConcreteLoginerFactory.getConcreteLoginer("博客园");
concreteLoginer_4.login(new User("董才才","654321"));
ConcreteLoginer concreteLoginer_5 = ConcreteLoginerFactory.getConcreteLoginer("博客园");
concreteLoginer_5.login(new User("董才才","654321"));
ConcreteLoginer concreteLoginer_6 = ConcreteLoginerFactory.getConcreteLoginer("博客园");
concreteLoginer_6.login(new User("董才才","654321"));
System.out.println("是否是同一个对象:" + ((concreteLoginer_4==concreteLoginer_5)&&(concreteLoginer_5 == concreteLoginer_6)));
// 工厂类中享元池中对象数量
System.out.println("享元池size:" + ConcreteLoginerFactory.getSize());
}
}
6、运行结果
4、总结
从上面代码和运行结果这可以看到,同一个登陆者登陆时是 "享" 用同一个登陆者对象。在享元对象池中只有两个对象。
享元模式优点
享元模式的外部状态相对独立,使得对象可以在不同的环境中被复用(共享对象可以适应不同的外部环境)
享元模式可共享相同或相似的细粒度对象,从而减少了内存消耗,同时降低了对象创建与垃圾回收的开销
享元模式缺点
外部状态由客户端保存,共享对象读取外部状态的开销可能比较大
享元模式要求将内部状态与外部状态分离,这使得程序的逻辑复杂化,同时也增加了状态维护成本
来源:https://www.cnblogs.com/dongxiucai/p/9460069.html


猜你喜欢
- 最近项目中的活动面板要做来回滚动卡牌预览效果,感觉自己来写的话,也能写,但是可能会比较耗时,看到Github上有开源的项目,于是就借用了,G
- jcasbin简介:jcasbin 是一个用 Java 语言打造的轻量级开源访问控制框架https://github.com/casbin/
- HashMap和HashTable,这二者的区别经常被别人问起,今天在此总结一下。(一)继承的历史不同public class
- 前言《摩尔庄园》前段时间上线, 持续超出市场预期,相信也有不错的收益。游戏好玩,所有玩家看到了前端,但是做一款游戏,离不开后台游戏服务器的支
- 本文实例讲述了Java数组队列概念与用法。分享给大家供大家参考,具体如下:一.队列的概念 (1)队列也是一种线性结构(2)相比数组
- 本文实例讲述了Android中使用Service实现后台发送邮件功能。分享给大家供大家参考,具体如下:程序如下:import android
- 一.工程文件二.Main.java主函数,实现类package ui;//主函数实现public class Main { &
- 最近过年发红包拜年成为一种新的潮流,作为程序猿对算法的好奇远远要大于对红包的好奇,这里介绍一种自己想到的一种随机红包分配策略,还请大家多多指
- 人机猜拳小游戏,只要用到的java面向对象的思维模式。本游戏中有游戏玩家和计算机两个参与者。玩家和计算机都有的属性是姓名和得分。共分为4个类
- 策略模式所谓策略其实就是做一件事情有很多很多的方法,比如说一个商场要搞促销,促销的方式有可能有很多:打折啊,满100返50啊、积分等等之类的
- 本文的目的是把json串转成map键值对存储,而且只存储叶节点的数据比如json数据如下:{responseHeader:{status:0
- 自定义一个漂亮实用的锁屏app,如果能赢得用户的认可,替换系统自带的锁屏,绝对是一个不小的日活入口。这段时间正好总结一下最近调研的Andro
- 本文实例讲述了C#线性渐变画刷LinearGradientBrush用法。分享给大家供大家参考。具体如下:using System;usin
- 在平常的开发过程中,我们的ListView可能不只是简单的显示下文本或者按钮,更多的是显示复杂的布局,这样的话,我们就得自己写布局和自定义a
- 前言在我们公司里,不同的服务之间通过Feign进行远程调用,但是,我们在尝试使调用可重试时遇到了一个小问题,Feign框架本身可以配置的自己
- 很多学习Android程序设计的人都会发现每个人对代码的写法都有不同的偏好,比较明显的就是对控件响应事件的写法的不同。因此本文就把这些写法总
- 前言多线程是我们开发过程中经常遇到的,也是必不可少需要掌握的。当我们知道需要进行多线程开发时首先需要知道的自然是如何实现多线程,也就是我们应
- Java中的wait/notify/notifyAll可用来实现线程间通信,是Object类的方法,这三个方法都是native方法,是平台相
- 前言有时候我们在项目中,会用到一些本地 jar 包文件,比如隔壁公司自己打包的;此时无法从maven远程仓库拉取;那么我们可以考虑把 jar
- 目录背景实体类示例一示例二背景以前常用的排序方式是通过实现Comparator接口来进行排序,写法相对来说比较复杂,使用Comparator