旧游无处不堪寻
无寻处,惟有少年心
设计模式-享元

享元模式(Flyweight Pattern)是一种结构型设计模式,它摒弃了在每个对象中保存所有数据的方式,通过共享多个对象所共有的相同状态,让你能在有限的内存容量中载入更多对象。

解释


对象的常量数据通常被称为内在状态,其位于对象中,其他对象只能读取但不能修改其数值。而对象的其他状态常常能被其他对象从外部改变,因此被称为外在状态。享元模式建议不在对象中存储外在状态,而是将其传递给依赖于它的一个特殊方法。程序只在对象中保存内在状态,以方便在不同情景下重用。

为了能更方便地访问各种享元,你可以创建一个工厂方法来管理已有享元对象的缓存池。工厂方法从客户端处接收目标享元对象的内在状态作为参数,如果它能在缓存池中找到所需享元,则将其返回给客户端,如果没有找到,它就会新建一个享元,并将其添加到缓存池中。

享元模式 UML


适用场景


  1. 仅在程序必须支持大量对象且没有足够的内存容量时使用享元模式

代码示例


using System;
namespace FlyweightPatternDemo
{
public class Flyweight
{
private Car _sharedState;

public Flyweight(Car car)
{
this._sharedState = car;
}

public void Operation(Car uniqueState)
{
string s = JsonConvert.SerializeObject(this._sharedState);
string u = JsonConvert.SerializeObject(uniqueState);
Console.WriteLine($"Flyweight: Displaying shared {s} and unique {u} state.");
}
}

public class FlyweightFactory
{
private List<Tuple<Flyweight, string>> flyweights = new List<Tuple<Flyweight, string>>();

public FlyweightFactory(params Car[] args)
{
foreach (var elem in args)
{
flyweights.Add(new Tuple<Flyweight, string>(new Flyweight(elem), this.getKey(elem)));
}
}

public string getKey(Car key)
{
List<string> elements = new List<string>();

elements.Add(key.Model);
elements.Add(key.Color);
elements.Add(key.Company);

if (key.Owner != null && key.Number != null)
{
elements.Add(key.Number);
elements.Add(key.Owner);
}

elements.Sort();

return string.Join("_", elements);
}

public Flyweight GetFlyweight(Car sharedState)
{
string key = this.getKey(sharedState);

if (flyweights.Where(t => t.Item2 == key).Count() == 0)
{
Console.WriteLine("FlyweightFactory: Can't find a flyweight, creating new one.");
this.flyweights.Add(new Tuple<Flyweight, string>(new Flyweight(sharedState), key));
}
else
{
Console.WriteLine("FlyweightFactory: Reusing existing flyweight.");
}
return this.flyweights.Where(t => t.Item2 == key).FirstOrDefault().Item1;
}

public void listFlyweights()
{
var count = flyweights.Count;
Console.WriteLine($"\nFlyweightFactory: I have {count} flyweights:");
foreach (var flyweight in flyweights)
{
Console.WriteLine(flyweight.Item2);
}
}
}

public class Car
{
public string Owner { get; set; }

public string Number { get; set; }

public string Company { get; set; }

public string Model { get; set; }

public string Color { get; set; }
}

class Program
{
static void Main(string[] args)
{
var factory = new FlyweightFactory(
new Car { Company = "Chevrolet", Model = "Camaro2018", Color = "pink" },
new Car { Company = "Mercedes Benz", Model = "C300", Color = "black" },
new Car { Company = "Mercedes Benz", Model = "C500", Color = "red" },
new Car { Company = "BMW", Model = "M5", Color = "red" },
new Car { Company = "BMW", Model = "X6", Color = "white" }
);
factory.listFlyweights();

addCarToPoliceDatabase(factory, new Car {
Number = "CL234IR",
Owner = "James Doe",
Company = "BMW",
Model = "M5",
Color = "red"
});

addCarToPoliceDatabase(factory, new Car {
Number = "CL234IR",
Owner = "James Doe",
Company = "BMW",
Model = "X1",
Color = "red"
});

factory.listFlyweights();
// output

// FlyweightFactory: I have 5 flyweights:
// Camaro2018_Chevrolet_pink
// black_C300_Mercedes Benz
// C500_Mercedes Benz_red
// BMW_M5_red
// BMW_white_X6

// Client: Adding a car to database.
// FlyweightFactory: Reusing existing flyweight.
// Flyweight: Displaying shared {"Owner":null,"Number":null,"Company":"BMW","Model":"M5","Color":"red"} and unique {"Owner":"James Doe","Number":"CL234IR","Company":"BMW","Model":"M5","Color":"red"} state.

// Client: Adding a car to database.
// FlyweightFactory: Can't find a flyweight, creating new one.
// Flyweight: Displaying shared {"Owner":null,"Number":null,"Company":"BMW","Model":"X1","Color":"red"} and unique {"Owner":"James Doe","Number":"CL234IR","Company":"BMW","Model":"X1","Color":"red"} state.

// FlyweightFactory: I have 6 flyweights:
// Camaro2018_Chevrolet_pink
// black_C300_Mercedes Benz
// C500_Mercedes Benz_red
// BMW_M5_red
// BMW_white_X6
// BMW_red_X1
}

public static void addCarToPoliceDatabase(FlyweightFactory factory, Car car)
{
Console.WriteLine("\nClient: Adding a car to database.");

var flyweight = factory.GetFlyweight(new Car {
Color = car.Color,
Model = car.Model,
Company = car.Company
});

flyweight.Operation(car);
}
}
}