# 享元模式 ( Flyweight )
## 用途
运用共享技术有效地支持大量细粒度的对象。
## 适用场景
Flyweight模式的有效性很大程度上取决于如何使用它以及在何处使用它。 当以下情况都成立时使用flyweight模式:
* 一个应用程序使用了大量的对象。
* 完全由于使用大量的对象,造成很大的存储开销。
* 对象的大多数状态都可变为外部状态。
* 如果删除对象的外部状态,那么可以用相对较少的共享对象取代很多组对象。
* 应用程序不依赖于对象标识。由于Flyweight对象可以被共享,对于概念上明显有别的对象,标识测试将返回真值。
## 模式要点
![](./uml/Flyweight.png)
### 组成部分
* Flyweight - 描述一个接口, 通过这个接口 flyweight 可以接受并作用于外部状态。
* ConcreteFlyweight — 实现 Flyweight 接口,并为内部状态(如果有的话)增加存储空间 。ConcreteFlyweight对象必须是可共享的。 它所存储的状态必须是内部的;即它必须独立于 ConcreteFlyweight 对象的场景。
* UnsharedConcreteFlyweight — 并非所有的 Flyweight 子类都需要被共享。Flyweight 接口使共享成为可能, 但它并不强制共享。 在 Flyweight 对象结构的某些层次,UnsharedConcreteFlyweight 对象通常将 ConcreteFlyweight 对象作为子节点。
* FlyweightFactory — 创建并管理 flyweight 对象。确保合理地共享 flyweight。当用户请求一个 flyweight 时,FlyweightFactory 对象提供一个已创建的实例或者创建一个(如果不存在的话)。
* Client:持有一个对 flyweight 的引用。计算或存储一个(多个)flyweight 的外部状态。
### 协作原理
* flyweight 执行时所需的状态必定是内部的或外部的。内部状态存储于 ConcreteFlyweight 对象之中;而外部对象则由 Client 对象存储或计算。当用户调用 flyweight 对象的操作时,将该状态传递给它。
* 用户不应直接对 ConcreteFlyweight 类进行实例化, 而只能从 FlyweightFactory 对象得到 ConcreteFlyweight 对象,这可以保证对它们适当地进行共享。
## 实例分析
![](./uml/Weapon.png)
出售武器的商店里摆满了各式的枪。许多枪的型号是一样的,所以不需要为每一个都创建新的对象。相反,一个对象实例可以表示多个货架项目,因此内存占用空间很小。
所有的枪都可以发出射击这个动作,因此定义一个枪的接口 Shooting
```
public interface Shooting {
void shoot();
}
```
商店里的枪可以大致分为手枪、步枪、狙击枪和冲锋枪,分别定义四种枪类,它们都实现 Shooting 接口
```
/**
* 手枪
*/
public class HandGun implements Shooting {
private static final Logger LOGGER = LoggerFactory.getLogger(HandGun.class);
@Override
public void shoot() {
LOGGER.info("手枪开火了(Hash={})", System.identityHashCode(this));
}
}
```
```
/**
* 步枪
*/
public class Musket implements Shooting {
private static final Logger LOGGER = LoggerFactory.getLogger(Musket.class);
@Override
public void shoot() {
LOGGER.info("步枪开火了(Hash={})", System.identityHashCode(this));
}
}
```
```
/**
* 狙击枪
*/
public class Sniper implements Shooting {
private static final Logger LOGGER = LoggerFactory.getLogger(Sniper.class);
@Override
public void shoot() {
LOGGER.info("狙击枪开火了(Hash={})", System.identityHashCode(this));
}
}
```
```
/**
* 冲锋枪
*/
public class Submachine implements Shooting {
private static final Logger LOGGER = LoggerFactory.getLogger(Submachine.class);
@Override
public void shoot() {
LOGGER.info("冲锋枪开火了(Hash={})", System.identityHashCode(this));
}
}
```
武器的生成由武器工厂类完成,同一种枪只存在一个对象
```
/**
* 武器工厂
*/
public class GunFactory {
private Map<GunType, Shooting> gunRepo;
public GunFactory() {
gunRepo = new EnumMap<GunType, Shooting>(GunType.class);
}
public Shooting createGun(GunType type) {
Shooting gun = gunRepo.get(type);
if (null == gun) {
switch (type) {
case HANDGUN: {
gun = new HandGun();
gunRepo.put(HANDGUN, gun);
break;
}
case MUSKET: {
gun = new Musket();
gunRepo.put(MUSKET, gun);
break;
}
case SNIPER: {
gun = new Sniper();
gunRepo.put(SNIPER, gun);
break;
}
case SUBMACHINE: {
gun = new Submachine();
gunRepo.put(SUBMACHINE, gun);
break;
}
}
}
return gun;
}
}
```
武器商店使用武器工厂获得各种枪,放到两个不同的货架上
```
/**
* 武器商店
*/
public class WeaponShop {
private static final Logger LOGGER = LoggerFactory.getLogger(WeaponShop.class);
private List<Shooting> shelfA;
private List<Shooting> shelfB;
public WeaponShop() {
shelfA = new ArrayList<>();
shelfB = new ArrayList<>();
fillShelves();
}
private void fillShelves() {
GunFactory factory = new GunFactory();
shelfA.add(factory.createGun(HANDGUN));
shelfA.add(factory.createGun(HANDGUN));
shelfA.add(factory.createGun(MUSKET));
shelfA.add(factory.createGun(MUSKET));
shelfA.add(factory.createGun(SNIPER));
shelfA.add(factory.createGun(SNIPER));
shelfA.add(factory.createGun(MUSKET));
shelfA.add(factory.createGun(HANDGUN));
shelfB.add(factory.createGun(SUBMACHINE));
shelfB.add(factory.createGun(SUBMACHINE));
shelfB.add(factory.createGun(SUBMACHINE));
shelfB.add(factory.createGun(SNIPER));
}
public final List<Shooting> getGunsOnShelfA() {
return Collections.unmodifiableList(shelfA);
}
public final List<Shooting> getGunOnShelfB() {
return Collections.unmodifiableList(shelfB);
}
public void enumrateShelves() {
enumerateShelfA();
enumerateShelfB();
}
private void enumerateShelfA() {
LOGGER.info("从A货架上拿走所有武器");
for (Shooting gun : shelfA) {
gun.shoot();
}
}
private void enumerateShelfB() {
LOGGER.info("从B货架上拿走所有武器");
for (Shooting gun : shelfB) {
gun.shoot();
}
}
}
```
使用
```
WeaponShop shop = new WeaponShop();
shop.enumrateShelves();
```
## 效果
使用 Flyweight 模式时, 传输、 查找和/或计算外部状态都会产生运行时的开销, 尤其当 flyweight 原先被存储为内部状态时。然而,空间上的节省抵消了这些开销。共享的 flyweight 越多, 空间节省也就越大。
存储节约由以下几个因素决定:
* 因为共享,实例总数减少的数目
* 对象内部状态的平均数目
* 外部状态是计算的还是存储的共享的Flyweight越多,存储节约也就越多。节约量随着共享状态的增多而增大。当对象使用大量的内部及外部状态,并且外部状态是计算出来的而非存储的时候,节约量将达到最大。所以,可以用两种方法来节约存储:用共享减少内部状态的消耗,用计算时间换取对外部状态的存储。
Flyweight 模式经常和 Composite 模式结合起来表示一个层次式结构, 这一层次式结构是一个共享叶节点的图。 共享的结果是, Flyweight的叶节点不能存储指向父节点的指针。而父节点的指针将传给 Flyweight 作为它的外部状态的一部分。这对于该层次结构中对象之间相互通讯的方式将产生很大的影响。
没有合适的资源?快使用搜索试试~ 我知道了~
温馨提示
设计模式(Design pattern)是用于面向对象程序设计的、有效提高代码重用效率、有着明确使用场景分类的程序设计规范的总结。使用设计模式的 目的:为了代码可重用性、让代码更容易被他人理解、保证代码可靠性。 设计模式使代码编写真正工程化;设计模式是软件工程的基石脉络,如同大厦 的结构一样。在程序设计中 引入设计模式可以提高代码的可读性和程序运行时的可靠性,使程序设计得到规范和统一。 设计模式代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临 的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。 设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了重用代码、让代码更容易被他人理解、 保证代码可靠性。 毫无疑问,设计模式于己于他人于系统都是多赢的,设计模式使代码编制真正工程化,设计模式是软件工程的基石,如同大厦的 一块块砖石一样。项目中合理地运用设计模式可以完美地解决很多问题,每种模式在现实中都有相应的原理来与之对应,每种模式都描述了一个在我
资源推荐
资源详情
资源评论
收起资源包目录
Java 实现的面向对象软件设计模式 (223个子文件)
Application.java 4KB
Flower.java 3KB
SingletonTest.java 3KB
CourseFacadeTest.java 3KB
ThreadSafeDoubleCheckLocking.java 3KB
WriterTest.java 3KB
Application.java 3KB
Person.java 3KB
WeaponShop.java 3KB
CarpenterOperationsTest.java 3KB
Writer.java 3KB
Application.java 3KB
PrototypeTest.java 3KB
Typist.java 2KB
CourseParticipator.java 2KB
FactoryMethodTest.java 2KB
AbstractFactoryTest.java 2KB
TestAdapter.java 2KB
Application.java 2KB
TestPerson.java 2KB
Application.java 2KB
Application.java 2KB
CourseFacade.java 2KB
BookShelf.java 2KB
FactoryTest.java 2KB
GunFactory.java 2KB
TeamFactoryImpl.java 2KB
Time.java 2KB
LearningMethod.java 2KB
Application.java 2KB
BookShelfIterator.java 2KB
AbstractPartyMember.java 2KB
Application.java 2KB
Application.java 2KB
Request.java 2KB
AncientWar.java 2KB
MordernWar.java 2KB
HammerSmithOperation.java 2KB
AbstractFont.java 2KB
Application.java 2KB
LazyInitializationDirector.java 2KB
NegativeLearinngMethod.java 2KB
Application.java 2KB
Application.java 2KB
PositiveLearningMethod.java 2KB
WarTest.java 2KB
HammerSmithOperationsTest.java 2KB
Northern.java 2KB
Application.java 2KB
ThreadSafeLazyLoadDirector.java 2KB
Southern.java 2KB
DiningRoomProxy.java 2KB
RequestHandler.java 2KB
ImpatientState.java 2KB
CharacterComposite.java 2KB
PartyImpl.java 2KB
Rubify.java 2KB
MultipleExpression.java 2KB
DivisionExpression.java 2KB
MinusExpression.java 2KB
PlusExpression.java 2KB
Enlarge.java 2KB
TenderEnemy.java 2KB
Application.java 2KB
IdleState.java 2KB
Student.java 2KB
IntrepidEnemy.java 2KB
Coder.java 2KB
CarpenterOperation.java 2KB
Director.java 2KB
Activity.java 2KB
EngineerVisitor.java 2KB
ManagerVisitor.java 2KB
BossVisitor.java 2KB
Gunny.java 2KB
Captain.java 2KB
Gunner.java 2KB
Application.java 2KB
NumberExpression.java 2KB
Application.java 2KB
Application.java 2KB
Item.java 1KB
BusinessMan.java 1KB
Application.java 1KB
ChineseFood.java 1KB
WesternFood.java 1KB
TransportationAirplane.java 1KB
Submachine.java 1KB
TransportationTrain.java 1KB
TransportationVehicle.java 1KB
AncientWarTest.java 1KB
Sniper.java 1KB
Application.java 1KB
Engineer.java 1KB
HandGun.java 1KB
Unit.java 1KB
MordernWarTest.java 1KB
Musket.java 1KB
Manager.java 1KB
DiningRoom.java 1KB
共 223 条
- 1
- 2
- 3
资源评论
嘻嘻爱编码
- 粉丝: 844
- 资源: 145
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
最新资源
- 通道处理过程的模拟通常涉及对通道处理机制的理解与实现.txt
- Flume进阶-自定义拦截器jar包
- Dubins曲线算法讲解和在运动规划中的使用.pdf
- 上市公司-股票性质数据-工具变量(民企、国企、央企)2003-2022年.dta
- 上市公司-股票性质数据-工具变量(民企、国企、央企)2003-2022年.xlsx
- Reeds+Shepp曲线算法讲解和实现.pdf
- 毕业设计基于SpringBoot+MyBatisPlus+MySQL+Vue的外卖配送信息系统源代码+数据库
- 词向量(Word Embeddings)是自然语言处理(NLP)领域的一种重要技术.txt
- Surfer,线性函数
- MyBatis 的动态 SQL 是其核心特性之一.txt
资源上传下载、课程学习等过程中有任何疑问或建议,欢迎提出宝贵意见哦~我们会及时处理!
点击此处反馈
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功