Flyweight设计模式主要解决面向对象的代价问题,一般不触及面向对象的抽象性问题。Flyweight采用对象共享的做法来降低系统中对象的个数,从而降低细粒度对象给系统带来的内存压力。在具体实现方面,要注意对象的状态处理。
Flyweight享元设计模式是一种结构型设计模式,它主要解决的问题是:由于(同类)对象的数量太大,采用面向对象时给系统带来了难以承受的内存开销。比如有这样一个场景:一个停车场中有1000辆汽车,我们所定义的汽车对象占用内存0.3M,那么要实例化1000辆就是300M。由此可见,在这种情况下采用一般的面向对象方式出现了大量细粒度的对象会很快充斥在系统中,从而带来很高的运行是代价(这里指的代价是内存开销的代价)。
GoF《设计模式》中说道:运用共享技术有效的支持大量细粒度的对象。
Flyweight享元设计模式是一种结构型设计模式,它主要解决的问题是:由于(同类)对象的数量太大,采用面向对象时给系统带来了难以承受的内存开销。比如有这样一个场景:一个停车场中有1000辆汽车,我们所定义的汽车对象占用内存0.3M,那么要实例化1000辆就是300M。由此可见,在这种情况下采用一般的面向对象方式出现了大量细粒度的对象会很快充斥在系统中,从而带来很高的运行是代价(这里指的代价是内存开销的代价)。
GoF《设计模式》中说道:运用共享技术有效的支持大量细粒度的对象。
Flyweight模式的结构大概是这样的:
(这张图是按照我的理解画出来的,如果有不对的地方还请帮我指正,谢谢),从图上可以看出Flyweight模式是将相同的对象存为一个,就是在FlyweightFactory中对于实例化对象的判断。这样,客户代码即使是调用1000000个对象,如果这些对象的种类只有一个的话,对于内存的分配上也只是分配了一个对象的空间。但是有一点我想要注意:就是对于引用对象来说,这样做,如果其中某一个对象发生变化,那么同类中的所有对象也会随之变化。
来看看程序,定义一个场景:有一个汽车类型,客户程序要实例化1000个,实例化后查看一下内存分配情况。
普通的面向对象方式:
class Class1
{
[STAThread]
static void Main(string[] args)
{
Console.WriteLine("实例化前:" + GC.GetTotalMemory(false));
ArrayList list = new ArrayList(1000);
for(int i = 0;i < 1000;i++)
{
Car car = new Car("4.2M","Wheel","NeedForSpeed","BMW","Black");
list.Add(car);
}
Console.WriteLine("实例化后:" + GC.GetTotalMemory(false));
Console.Read();
}
}
public class Car
{
private string body;
private string wheel;
private string engine;
private string brand;
private string color;
public string Body
{
get{return body;}
set{body = value;}
}
public string Wheel
{
get{return wheel;}
set{wheel = value;}
}
public string Engine
{
get{return engine;}
set{engine = value;}
}
public string Brand
{
get{return brand;}
set{brand = value;}
}
public string Color
{
get{return color;}
set{color = value;}
}
public Car(string body,string wheel,string engine,string brand,string color)
{
Body = body;
Wheel = wheel;
Engine = engine;
Brand = brand;
Color = color;
}
}
内存分配情况如下:
实例化前:16384
实例化后:65536
然后再用Flyweight模式方式程序做一下比较:
class Class1
{
[STAThread]
static void Main(string[] args)
{
Console.WriteLine("实例化前:" + GC.GetTotalMemory(false));
ArrayList list = new ArrayList(1000);
for(int i = 0;i < 1000;i++)
{
FlyWeightCar car = FlyWeightFactory.CreateInit("4.2M","Wheel","NeedForSpeed","BMW","Black");
list.Add(car);
}
Console.WriteLine("实例化后:" + GC.GetTotalMemory(false));
Console.Read();
}
}
public class FlyWeightFactory
{
private static FlyWeightCar car;
private static Hashtable table = new Hashtable();
public static FlyWeightCar CreateInit(string body,string wheel,string engine,string brand,string color)
{
if(table[brand] != null)
{
car = (FlyWeightCar)table[brand];
}
else
{
car = new FlyWeightCar();
car.Brand = brand;
car.CarBody = new CarBody(body,wheel,engine,color);
table.Add(brand,car);
}
return car;
}
}
public class FlyWeightCar
{
private string brand;
public string Brand
{
get
{
return brand;
}
set
{
brand = value;
}
}
private CarBody carbody;
public CarBody CarBody
{
get
{
return carbody;
}
set
{
this.carbody = value;
}
}
}
public class CarBody
{
private string body;
private string wheel;
private string engine;
private string color;
public string Body
{
get{return body;}
set{body = value;}
}
public string Wheel
{
get{return wheel;}
set{wheel = value;}
}
public string Engine
{
get{return engine;}
set{engine = value;}
}
public string Color
{
get{return color;}
set{color = value;}
}
public CarBody(string body,string wheel,string engine,string color)
{
Body = body;
Wheel = wheel;
Engine = engine;
Color = color;
}
}
内存分配情况:
实例化前:16384
实例化后:40960
从数字上不难看出内存分配的容量节省了不少,而且随着数量的增加,差距会更大,当然我也测试了一下数量减少的情况,当我实例化100个对象是结果是普通方式的内存分配更小一些,所以,在使用时,我们一定要对所实例化对象的个数进行评估,否则的话会适得其反。
Flyweight模式的几个要点:
1、面向对象很好的解决了抽象性的问题,但是作为一个运行在机器中的程序实体,我们需要考虑对象的代价问题。Flyweight设计模式主要解决面向对象的代价问题,一般不触及面向对象的抽象性问题。
2、Flyweight采用对象共享的做法来降低系统中对象的个数,从而降低细粒度对象给系统带来的内存压力。在具体实现方面,要注意对象的状态处理。
3、对象的数量太大从而导致对象内存开销加大(这个数量要经过评估,而不能凭空臆断)