oc中的 tableview和collectionView 是享元模式的一个例子, cell有一个内存池 , 我们先从内存池是取值,如果没有才创建, 所以不论多少cell,实际系统分配的内存也就很少,只有屏幕上显示的那十几个真正分配了内存, 其他不可见的cell都没有创建UIView内存.
还有一个,就直播和视频网站上的弹幕,也需要应用享元模式,一个视频有几千条弹幕的话,创建那么多view,label是不现实的,只能使用享元模式,而且还要做一个事情,比如高峰时全屏都是弹幕,但是其余时候只有几条弹幕,就需要有一定的衰退期,过了衰退期回收内存.
享元模式(英语:Flyweight Pattern)是一种软件设计模式。它使用共享物件,用来尽可能减少内存使用量以及分享资讯给尽可能多的相似物件;它适合用于当大量物件只是重复因而导致无法令人接受的使用大量内存。通常物件中的部分状态是可以分享。常见做法是把它们放在外部数据结构,当需要使用时再将它们传递给享元。
享元模式(Flyweight):运用共享的技术有效地支持大量细粒度的对象。
- 抽象享元角色(Flyweight):此角色是所有的具体享元类的超类,为这些类规定出需要实现的公共接口或抽象类。那些需要外部状态(External State)的操作可以通过方法的参数传入。抽象享元的接口使得享元变得可能,但是并不强制子类实行共享,因此并非所有的享元对象都是可以共享的。
- 具体享元(ConcreteFlyweight)角色:实现抽象享元角色所规定的接口。如果有内部状态的话,必须负责为内部状态提供存储空间。享元对象的内部状态必须与对象所处的周围环境无关,从而使得享元对象可以在系统内共享。有时候具体享元角色又叫做单纯具体享元角色,因为复合享元角色是由单纯具体享元角色通过复合而成的。
- 复合享元(UnsharableFlyweight)角色:复合享元角色所代表的对象是不可以共享的,但是一个复合享元对象可以分解成为多个本身是单纯享元对象的组合。复合享元角色又称作不可共享的享元对象。这个角色一般很少使用。
- 享元工厂(FlyweightFactoiy)角色:本角色负责创建和管理享元角色。本角色必须保证享元对象可以被系统适当地共享。当一个客户端对象请求一个享元对象的时候,享元工厂角色需要检查系统中是否已经有一个符合要求的享元对象,如果已经有了,享元工厂角色就应当提供这个已有的享元对象;如果系统中没有一个适当的享元对象的话,享元工厂角色就应当创建一个新的合适的享元对象。
- 客户端(Client)角色:本角色还需要自行存储所有享元对象的外部状态。
内部状态与外部状态:在享元对象内部并且不会随着环境改变而改变的共享部分,可以称之为享元对象的内部状态,反之随着环境改变而改变的,不可共享的状态称之为外部状态。
公共交换电话网的使用方式就是生活中常见的享元模式的例子。公共交换电话网中的一些资源,例如拨号音发生器、振铃发生器和拨号接收器,都是必须由所有用户共享的,不可能为每一个人都配备一套这样的资源,否则公共交换电话网的资源开销也太大了。当一个用户拿起听筒打电话时,他根本不需要知道到底使用了多少资源,对用户而言所有的事情就是有拨号音,拨打号码,拨通电话就行了。所以,就有很有人会共用一套资源,非常节省,这就是享元模式的基本思想。
//用户
public class User {
private string name;
public User(string name)
{
this.name = name;
}
public string Name
{
get { return name; }
}
}
//网站工厂
class WebSiteFactory
{
private Hashtable flyweights = new Hashtable();
//获得网站分类
public WebSite GetWebSiteCategory(string key)
{
if (!flyweights.ContainsKey(key))
flyweights.Add(key, new ConcreteWebSite(key));
return ((WebSite)flyweights[key]);
}
//获得网站分类总数
public int GetWebSiteCount()
{
return flyweights.Count;
}
}
//网站
abstract class WebSite
{
public abstract void Use(User user);
}
//具体的网站
class ConcreteWebSite : WebSite
{
private string name = "";
public ConcreteWebSite(string name)
{
this.name = name;
}
public override void Use(User user)
{
Console.WriteLine("网站分类:" + name + " 用户:" + user.Name);
}
}
static void Main(string[] args)
{
WebSiteFactory f = new WebSiteFactory();
WebSite fx = f.GetWebSiteCategory("产品展示");
fx.Use(new User("小菜"));
WebSite fy = f.GetWebSiteCategory("产品展示");
fy.Use(new User("大鸟"));
WebSite fz = f.GetWebSiteCategory("产品展示");
fz.Use(new User("娇娇"));
WebSite fl = f.GetWebSiteCategory("博客");
fl.Use(new User("老顽童"));
WebSite fm = f.GetWebSiteCategory("博客");
fm.Use(new User("桃谷六仙"));
WebSite fn = f.GetWebSiteCategory("博客");
fn.Use(new User("南海鳄神"));
Console.WriteLine("得到网站分类总数为 {0}", f.GetWebSiteCount());
//string titleA = "大话设计模式";
//string titleB = "大话设计模式";
//Console.WriteLine(Object.ReferenceEquals(titleA, titleB)); // 系统采用享元模式的例子
Console.Read();
}
适用性
Flyweight模式的有效性很大程度上取决于如何使用它以及在何处使用它。当以下情况都成立时使用Flyweight模式。
1)一个应用程序使用了大量的对象。
2)完全由于使用大量的对象,造成很大的存储开销。
3)对象的大多数状态都可变为外部状态。
4)如果删除对象的外部状态,那么可以用相对较少的共享对象取代很多组对象。
5)应用程序不依赖对象标识。
优缺点
1)享元模式使得系统更加复杂。为了使对象可以共享,需要将一些状态外部化,这使得程序的逻辑复杂化。
2)享元模式将享元对象的状态外部化,而读取外部状态使得运行时间稍微变长。