设计模式学习总结-享元模式(Flyweight Method)
问题:
在面向对象的设计中和开发中,经常会遇到,同一类型的对象(由同一个class new出)会在不同的场景中出现多次,可是由于应用场景不同,他们的状态(属性值)又有所不同,但是他们的大多数属性又都是相同的,我们需要在不同的应用场景中创建大量具有不同状态的对象,然而大量的对象会造成很大的系统存储开销,以及对象的创建时的性能开销。
定义:
享元模式(又叫:轻量级模式)是一种结构型模式,运用共享技术有效地支持大量细粒度的对象。
意图:
抽出多个同一对象在不同应用场景中的共同状态(intrinsic,在不同的应用场景中相对不变的),封装在一个可以被共享的元类中(Concrete Flyweight),并提供一个修改需要变化状态的访问接口,用于在不同场景中,修改不被共享的外部状态(extrinsic),Flyweight Factory拥有一个管理、存储对象的“仓库”(或者叫对象池),当接收到客户端请求时会遍历对象池中的对象,如果已经存在则直接返回给Client,否则创建一个新的对象并将这个对象加入到池中然后返回给客户端,从而减少了大量的对象创建工作。
参与者:
•抽象享元(Flyweight)角色:
此角色是所有的具体享元类的基类,为这些类规定出需要实现的公共接口。那些需要外部状态(extrinsic)的操作可以通过调用享元方法以参数形式传入。
•具体享元(Concrete Flyweight)角色:
实现抽象享元角色所规定的接口。如果有内部状态(intrinsic)的话,必须负责为内部状态(intrinsic)提供存储空间。享元对象的内部状态(intrinsic)必须与对象所处的周围环境无关,从而使得享元对象可以在系统内共享的。
•享元工厂(Flyweight Factory)角色:
本角色负责创建和管理享元角色。本角色必须保证享元对象可以被系统适当地共享。当一个客户端对象调用一个享元对象的时候,享元工厂角色会检查系统中是否已经有一个符合要求的享元对象。如果已经有了,享元工厂角色就应当提供这个已有的享元对象;如果系统中没有一个适当的享元对象的话,享元工厂角色就应当创建一个合适的享元对象。
•客户端(Client)角色:
本角色需要维护一个对所有享元对象的引用。本角色需要自行存储所有享元对象的外部状态(Extrinsic)。
UML:
实例说明:
诺基亚手机工厂
有总部工厂生产,然后转给各个分公司贴牌上市
uml图如下:
代码:
/// 抽象享元(Flyweight)角色
/// </summary>
public abstract class PhoneModel
{
// 共享元素
public string Name { get; set; }
public string Net { get; set; }
public string Size { get; set; }
//不共享状态的元素
protected string ProductionPlace { get; set; }
// 提供一个修改不共享状态修改的方法。
public abstract void Change(string productionPlace);
}
/// <summary>
/// 具体享元(Concrete Flyweight)角色
/// </summary>
public class N8Object : PhoneModel
{
public N8Object()
{
this.Name = "N8";
this.Net = "CDMA";
this.Size = "98*20*10";
}
public override void Change(string productionPlace)
{
this.ProductionPlace = productionPlace;
}
}
/// <summary>
/// 具体享元(Concrete Flyweight)角色
/// </summary>
public class N9Object : PhoneModel
{
public N9Object()
{
this.Name = "N9";
this.Net = "WCDMA";
this.Size = "100*20*10";
}
public override void Change(string productionPlace)
{
this.ProductionPlace = productionPlace;
System.Console.WriteLine("已修改N9的产地为" + productionPlace);
}
}
/// <summary>
/// 享元工厂(Flyweight Factory)角色
/// </summary>
public class FlyweightFactory
{
Dictionary<string, PhoneModel> PhoneList = new Dictionary<string, PhoneModel>();
public PhoneModel GetPhoneObject(string name)
{
if (!PhoneList.ContainsKey(name))
{
PhoneModel phone = null;
switch (name)
{
case "N8":
phone = new N8Object(); break;
case "N9":
phone = new N9Object(); break;
}
PhoneList.Add(name, phone);
}
return PhoneList[name];
}
}
/// <summary>
/// 客户端测试
/// </summary>
public void DecoratorTest()
{
FlyweightFactory factory = new FlyweightFactory();
var n8= factory.GetPhoneObject("N8");
n8.Change("新西兰");
}
优点:
•减少处理的对象数量,从而减少大量的对象存储开销,以及对象的创建时的性能开销。
缺点:
•需要由客户端维护对象的外部状态,如果外部状态的数据量大,传递、查找、计算会变得非常复杂。
适用场合:
•系统需要存在大量的对象而不同场景下的对象拥有大量的共享状态。
PS:
享元工厂中维护了享元实例的列表,同样也需要占用资源,所以如果享元的实例不是非常多的话(一个对象的应用场景比较少),不适合使用享元模式
如果对象拥有非常多的独立状态,比共享状态还要多不适合运用享元模式。因为维持大量的外部状态不但会使逻辑复杂而且并不能节约资源。