C++设计模式-Flyweight享元模式
Flyweight享元模式
作用:运用共享技术有效地支持大量细粒度的对象。
内部状态intrinsic和外部状态extrinsic:
1)Flyweight模式中,最重要的是将对象分解成intrinsic和extrinsic两部分。
2)内部状态:在享元对象内部并且不会随环境改变而改变的共享部分,可以称为是享元对象的内部状态
3)外部状态:而随环境改变而改变的,取决于应用环境,或是实时数据,这些不可以共享的东西就是外部状态了。
4)内部状态和外部状态之间的区别:
在Flyweight模式应用中,通常修改的是外部状态属性,而内部状态属性一般都是用于参考或计算时引用。
Flyweight执行时所需的状态必定是内部的或外部的。内部状态存储于ConcreteFlyweight对象之中;而外部状态则由Client对象存储或计算。当用户调用Flyweight对象的操作时,将该状态传递给它。
以文字处理软件为例:
内部状态存储于flyweight中,它包含了独立于flyweight场景的信息,这些信息使得flyweight可以被共享。如字符代码,字符大小……
外部状态取决于flyweight场景,并根据场景而变化,因此不可共享。用户对象负责在必要的时候将外部状态传递给flyweight,如字符位置,字符颜色……
UML图:
解析:
Flyweight:享元类的基类,定义一个接口,通过这个接口Flyweight可以接受并作用于外部状态。
ConcreteFlyweight:实现Flyweight接口, 并为内部状态( 如果有的话) 增加存储空间。ConcreteFlyweight对象必须是可共享的。它所存储的状态必须是内部的(intrinsic);即,它必须独立于ConcreteFlyweight对象的场景。
UnsharedConcreteFlyweight:并非所有的Flyweight子类都需要被共享。Flyweight接口使共享成为可能,但它并不强制共享。在Flyweight对象结构的某些层次,UnsharedConcreteFlyweight对象通常将ConcreteFlyweight对象作为子节点。
FlyweightFactory:
1) 创建并管理Flyweight对象。
2)确保合理地共享Flyweight。当用户请求一个Flyweight时,FlyweightFactory对象提供一个已创建的实例或者创建一个(如果不存在的话)
Client
1)维持一个对Flyweight的引用。
2)计算或存储一个(多个)Flyweight的外部状态。
分析:
享元模式可以避免大量非常相似类的开销。在程序设计中,有时需要生成大量细粒度的类实例来表示数据。如果能发现这些实例数据除了几个参数外基本都是相同的。有时就能够大幅度地减少实例化的类的数量。如果能把那些参数移到类实例的外面,在方法调用时将它们传递进来,就可以通过共享大幅度地减少单个实例的数目。
比如在文档编辑器的设计过程中,我们如果为没有字母创建一个对象的话,系统可能会因为大量的对象而造成存储开销的浪费。例如一个字母“a”在文档中出现了100000次,而实际上我们可以让这一万个字母“a”共享一个对象,当然因为在不同的位置可能字母“a”有不同的显示效果(例如字体和大小等设置不同),在这种情况我们可以为将对象的状态分为“外部状态”和“内部状态”,将可以被共享(不会变化)的状态作为内部状态存储在对象中,而外部对象(例如上面提到的字体、大小等)我们可以在适当的时候将外部对象最为参数传递给对象(例如在显示的时候,将字体、大小等信息传递给对象)。
Flyweight的内部状态是用来共享的,Flyweightfactory负责维护一个Flyweight池(存放内部状态的对象),当客户端请求一个共享Flyweight时,这个factory首先搜索池中是否已经有可适用的,如果有,factory只是简单返回送出这个对象,否则,创建一个新的对象,加入到池中,再返回送出这个对象.池为重复或可共享的对象、属性设置一个缓冲,称为内部状态。这些内部状态一般情况下都是不可修改的,也就是在第一个对象、属性被创建后,就不会去修改了(否则就没意义了)。
Flyweight 对对象的内部状态进行共享,只为每种内部状态创建一个实例,对内部状态使用了单例模式。
用户不应直接对ConcreteFlyweight类进行实例化,而只能从FlyweightFactory对象得到ConcreteFlyweight对象,这可以保证对它们适当地进行共享。
存储节约由以下几个因素决定:
1) 因为共享,实例总数减少的数目
2) 对象内部状态的平均数目
3) 外部状态是计算的还是存储的
实现要点
1)享元工厂维护一张享元实例表。
2)享元不可共享的状态需要在外部维护。即删除外部状态:该模式的可用性在很大程度上取决于是否容易识别外部状态并将它从共享对象中删除。
3)按照需求可以对享元角色进行抽象。
4)管理共享对象:引用计数和垃圾回收……
何时采用
1)如果一个应用程序使用了大量的对象,而大量的这些对象造成了很大的存储开销时就应该考虑使用;
2)还有就是对象的大多数状态可变为外部状态,如果删除对象的外部状态,那么可以用相对较少的共享对象取代很多组对象,此时可以考虑所使用享元模式。
3)系统中有大量耗费了大量内存的细粒度对象,并且对外界来说这些对没有任何差别的(或者说经过改造后可以是没有差别的)。
在文档编辑器例子中如果一个字符对应一个对象,那么一篇文档所要容纳的对象将是非常的庞大耗费大量的内存。而实际组成文档的字符是有限的,是由这些字符不同的组合和排列得到的。所以需要共享,将基本的字符进行共享,将使得字符对象变得有限。
代码如下:
Flyweight.h
#ifndef _FLYWEIGHT_H_
#define _FLYWEIGHT_H_ #include <string>
#include <vector> using namespace std; //基类,定义操作接口Operation
class Flyweight
{
public:
//操作外部状态extrinsicState
virtual void Operation(const string& extrinsicState)=;
string GetIntrinsicState();
virtual ~Flyweight();
protected:
Flyweight(string intrinsicState);
private:
//内部状态,也可以放在ConcreteFlyweight中
string _intrinsicState;
}; class ConcreteFlyweight:public Flyweight
{
public:
//实现接口函数
virtual void Operation(const string& extrinsicState);
ConcreteFlyweight(string intrinsicState);
~ConcreteFlyweight();
}; class UnsharedConcreteFlyweight:public Flyweight
{
public:
virtual void Operation(const string& extrinsicState);
UnsharedConcreteFlyweight(string intrinsicState);
~UnsharedConcreteFlyweight();
}; class FlyweightFactory
{
public:
FlyweightFactory();
~FlyweightFactory();
//获得一个请求的Flyweight对象
Flyweight* GetFlyweight(string key);
//获取容器中存储的对象数量
void GetFlyweightCount();
protected:
private:
//保存内部状态对象的容器
vector<Flyweight*> m_vecFly;
};
#endif
Flyweight.cpp
#include "Flyweight.h"
#include <iostream> using namespace std; Flyweight::Flyweight(string intrinsicState)
{
this->_intrinsicState = intrinsicState;
} Flyweight::~Flyweight()
{} string Flyweight::GetIntrinsicState()
{
return this->_intrinsicState;
} ConcreteFlyweight::ConcreteFlyweight(string intrinsicState):Flyweight(intrinsicState)
{
} ConcreteFlyweight::~ConcreteFlyweight()
{
} void ConcreteFlyweight::Operation(const string& extrinsicState)
{
cout << this->GetIntrinsicState() << endl;
cout << extrinsicState << endl;
} UnsharedConcreteFlyweight::UnsharedConcreteFlyweight(string intrinsicState):Flyweight(intrinsicState)
{
} UnsharedConcreteFlyweight::~UnsharedConcreteFlyweight()
{
} void UnsharedConcreteFlyweight::Operation(const string& extrinsicState)
{
cout << "extrinsicState" << endl;
} FlyweightFactory::FlyweightFactory()
{} FlyweightFactory::~FlyweightFactory()
{} //若该对象已存在,直接返回,否则新建一个对象,存入容器中,再返回
Flyweight* FlyweightFactory::GetFlyweight(string key)
{
vector<Flyweight*>::iterator iter = this->m_vecFly.begin();
for(;iter!= this->m_vecFly.end();iter++)
{
if((*iter)->GetIntrinsicState() == key)
{
return *iter;
}
}
Flyweight* fly = new ConcreteFlyweight(key);
this->m_vecFly.push_back(fly);
return fly;
} void FlyweightFactory::GetFlyweightCount()
{
cout << this->m_vecFly.size() << endl;
}
main.cpp
#include "Flyweight.h"
#include <iostream>
#include <string> using namespace std; int main()
{
//外部状态extrinsicState
string extrinsicState = "ext"; //工厂对象,工厂对象
FlyweightFactory* fc = new FlyweightFactory(); //向工厂申请一个Flyweight对象,且该对象的内部状态值为“hello”
Flyweight* fly = fc->GetFlyweight("hello"); Flyweight* fly1 = fc->GetFlyweight("hello"); //应用外部状态
fly->Operation(extrinsicState); fc->GetFlyweightCount(); return ;
}
另外一个文本处理的例子
参考:
http://www.dofactory.com/Patterns/PatternFlyweight.aspx
http://sourcemaking.com/design_patterns/flyweight/c%2523#
代码如下:
Document.h
#ifndef _DOCUMENT_H_
#define _DOCUMENT_H_ #include <string>
#include <vector> using namespace std; // The 'Flyweight' abstract class
class character
{
public:
//析构函数
virtual ~character();
//应用外部状态
virtual void Display(int width,int height,int ascent,int descent,int pointSize)=;
//获取内部状态
virtual char GetSymbol()=;
protected:
/*-----内部状态---------*/
char symbol;
/*----------------------/ /*-----外部状态---------*/
int width;
int height;
int ascent;
int descent;
int pointSize;
/*----------------------*/
//构造函数
character(char c);
}; //A 'ConcreteFlyweight' class
class characterA:public character
{
public:
characterA(char c);
~characterA();
virtual void Display(int width,int height,int ascent,int descent,int pointSize);
virtual char GetSymbol();
protected:
private:
}; //B 'ConcreteFlyweight' class
class characterB:public character
{
public:
characterB(char c);
~characterB();
virtual void Display(int width,int height,int ascent,int descent,int pointSize);
protected:
private:
}; //C 'ConcreteFlyweight' class
class characterC:public character
{
public:
characterC(char c);
~characterC();
virtual void Display(int width,int height,int ascent,int descent,int pointSize);
protected:
private:
}; //D 'ConcreteFlyweight' class
class characterD:public character
{
public:
characterD(char c);
~characterD();
virtual void Display(int width,int height,int ascent,int descent,int pointSize);
protected:
private:
};
/*
...
*/ //The 'FlyweightFactory' class
class characterFactory
{
public:
characterFactory();
~characterFactory();
//申请一个character对象
character* GetCharacter(char);
//获取存储的character*数量
vector<character*>::size_type GetCount();
private:
//保存character*的容器,可以换成List等其它容器
vector<character*> m_vecCharacter;
}; #endif
Document.cpp
#include "Document.h"
#include <iostream> character::character(char c)
{
this->symbol = c;
} character::~character()
{
} characterA::characterA(char c):character(c)
{
} characterA::~characterA()
{
} char characterA::GetSymbol()
{
return this->symbol;
} void characterA::Display(int width,int height,int ascent,int descent,int pointSize)
{
//接收并作用外部状态
this->ascent = ascent;
this->descent = descent;
this->height = height;
this->pointSize = pointSize;
this->width = width; cout << this->symbol <<" "
<< this->ascent <<" "
<< this->descent <<" "
<< this->height <<" "
<< this->pointSize <<" "
<< this->width << endl;
} characterFactory::characterFactory()
{} characterFactory::~characterFactory()
{} character* characterFactory::GetCharacter(char c)
{
vector<character*>::iterator iter = this->m_vecCharacter.begin();
for(;iter != this->m_vecCharacter.end();iter++)
{
if((*iter)->GetSymbol() == c)
{
return *iter;
}
}
character* pf = new characterA(c);
this->m_vecCharacter.push_back(pf);
return pf;
} vector<character*>::size_type characterFactory::GetCount()
{
return this->m_vecCharacter.size();
}
main.cpp
#include "Flyweight.h"
#include "Document.h"
#include <iostream>
#include <string> using namespace std; int main()
{
//存储外部状态
int ascent = ;
int descent = ;
int height = ;
int width = ;
int pointSize = ; string test = "AABCDDEFGHI";
string::iterator it = test.begin();
characterFactory* pcF = new characterFactory();
for(;it!=test.end();it++)
{
pointSize++;
char c = *it;
//申请一个character对象
character* charc= pcF->GetCharacter(c);
//应用外部状态
charc->Display(width,height,ascent,descent,pointSize);
}
vector<character*>::size_type sizeChar = pcF->GetCount();
cout << "count:" << sizeChar << endl; return ;
}
C++设计模式-Flyweight享元模式的更多相关文章
- 乐在其中设计模式(C#) - 享元模式(Flyweight Pattern)
原文:乐在其中设计模式(C#) - 享元模式(Flyweight Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 享元模式(Flyweight Pattern) 作者:weba ...
- 设计模式之享元模式(Flyweight)摘录
23种GOF设计模式一般分为三大类:创建型模式.结构型模式.行为模式. 创建型模式抽象了实例化过程,它们帮助一个系统独立于怎样创建.组合和表示它的那些对象.一个类创建型模式使用继承改变被实例化的类,而 ...
- 设计模式(11)--Flyweight(享元模式)--结构型
作者QQ:1095737364 QQ群:123300273 欢迎加入! 1.模式定义: 享元模式是对象的结构模式.享元模式以共享的方式高效地支持大量的细粒度对象. 2.模式特点: 享元模 ...
- 设计模式之flyweight享元模式
运用共享技术支持大量细粒度对象的使用 Flyweight模式(享元) Java深入到一定程度,就不可避免的碰到设计模式这一概念,了解设计模式,将使自己对java中的接口或抽象类应用有更深的理解.设计模 ...
- 设计模式11: Flyweight 享元模式(结构型模式)
Flyweight 享元模式(结构型模式) 面向对象的代价 面向对象很好的解决了系统抽象性的问题,同时在大多数情况下也不会损及系统的性能.但是,在某些特殊应用中,由于对象的数量太大,采用面向对象会给系 ...
- 20、FlyWeight 享元模式
池化的思想 1.Flyweight享元模式 运用共享技术有效地支持大量细粒度对象的复用.系统只使用少量的对象,而这些对象都很相似,状态变化很小,可以实现对象的多次复用.由于享元模式要求能够共享的对象必 ...
- python设计模式之享元模式
python设计模式之享元模式 由于对象创建的开销,面向对象的系统可能会面临性能问题.性能问题通常在资源受限的嵌入式系统中出现,比如智能手机和平板电脑.大型复杂系统中也可能会出现同样的问题,因为要在其 ...
- Flyweight享元模式(结构型模式)
1.面向对象的缺点 虽然OOP能很好的解决系统抽象的问题,并且在大多数的情况下,也不会损失系统的性能.但是在某些特殊的业务下,由于对象的数量太多,采用面向对象会给系统带来难以承受的内存开销.示例代码如 ...
- 设计模式(十二): Flyweight享元模式 -- 结构型模式
说明: 相对于其它模式,Flyweight模式在PHP实现似乎没有太大的意义,因为PHP的生命周期就在一个请求,请求执行完了,php占用的资源都被释放.我们只是为了学习而简单做了介绍. 1. 概述 面 ...
随机推荐
- iOS测试常见崩溃
什么是崩溃日志,从哪里能得它? iOS设备上的应用闪退时,操作系统会生成一个崩溃报告,也叫崩溃日志,保存在设备上.崩溃日志上有很多有用的信息,包括应用是什么情况下闪退的.通常,上面有每个正在执行线程的 ...
- 水平ListView类
package com.hztbc.android.HorizontalListView; /* * HorizontalListView.java v1.5 * * * The MIT Licen ...
- kernel/vsprintf.c
/* * linux/kernel/vsprintf.c * * Copyright (C) 1991, 1992 Linus Torvalds */ /* vsprintf.c -- Lars ...
- Java操作redis简单示例
第一:安装Redis 首先我们要安装Redis,就像我们操作数据库一样,在操作之前肯定要先创建好数据库的环境. Redis的下载可以百度一下,或者打开下面的下载链接: https:/ ...
- java return
return语句的作用: 1.返回一个值,可以是任意类型的 2.使程序返回到操作系统,或者说是代表"已经做完,离开此方法" ---------------------------- ...
- 自定义响应结构 Json格式转换 工具类
import java.util.List; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterx ...
- PHPExcel 导出时乱码
今天遇到了个奇怪的问题..猜测应该是因为php文件的编码造成的,但是没有解决办法. 问题是,用PHPEXCEL导出文件的时候,相同的代码在一个文件中可以导出,在另一个文件中却不行.. 最后没有办法,只 ...
- CMD:在当前文件夹下打开cmd命令
对于电脑老鸟而言,在使用windows系统的过程中,经常需要在cmd窗口中输入一些命令进行操作.但是如果每一次都是在cmd窗口中用CD命令进行相应的文件夹目录,实在不便,尤其是在文件夹层次比较多而且带 ...
- How To: Samba4 AD PDC + Windows XP, Vista and 7
dnsmasq If you've been struggling with Samba3 domain controllers and NT4 style domains working with ...
- linux 并发 RCU
What is RCU, Fundamentally? https://lwn.net/Articles/262464/ If you can fill the unforgiving secondw ...