享元模式(Flyweight Pattern)是一种结构型设计模式,首先说享元的含义,享元即有共享单元的意思。它主要解决的问题是创建大量相似对象时的内存开销问题。该模式通过共享具有相同状态的对象来减少内存使用量
当一个系统中存在大量重复对象,若这些重复的对象是不可变对象,就能利用享元模式将对象设计成享元,在内存中只保留一份实例,供引用。这就减少内存中对象的数量,最终节省内存。
"不可变对象":一旦通过构造器初始化完成后,其状态(对象的成员变量或属性)就不会再被修改。所以,不可变对象不能暴露任何 set()等修改内部状态的方法。之所以要求享元是不可变对象,是因为它会被多处代码共享使用,避免一处代码对享元进行了修改,影响到其他使用它的代码。
共享对象的状态分为 内部状态 与 外部状态 ,内部状态在各个对象间共享,而外部状态由客户端传入,这一点一定要牢记
优点
大大减少对象的创建,降低系统的内存,使效率提高。
缺点
提高了系统的复杂度,需要分离出外部状态和内部状态,而且外部状态具有固有化的性质,不应该随着内部状态的变化而变化,否则会造成系统的混乱。
由上图可以看出由 3 部分组成
Flyweight:享元接口,定义所有对象共享的操作
ConcreteFlyweight:具体的要被共享的对象,其一般是一个不可变类,内部只保存需要共享的内部状态,它可能不止一个。
FlyweightFactory:负责给客户端提供共享对象
其实在示例上我一直想靠游戏来进行说明,但是很抱歉并没有那一款游戏符合。而网络上更多的是以五子棋为例,那就以五子棋为例吧。其实这是因为这里需要大量的棋子对象,它们除了颜色有黑白之分,摆放的位置不同其他都一样,非常适合使用享元模式
客户端代码
program Flyweight;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils,
System.TypInfo,
UnitFlyweight in 'UnitFlyweight.pas';
var
r1: IChess;
begin
try
var ChessFactory := TChessFactory.Create();
//下黑子
var BackChess1 := ChessFactory.GetChess(Tcolor.BLACK);
BackChess1.Draw(1, 2);
//下白子
var WhiteChess1 := ChessFactory.GetChess(Tcolor.WHITE);
WhiteChess1.Draw(2, 2);
//下黑子
var BackChess2 := ChessFactory.GetChess(Tcolor.BLACK);
BackChess2.Draw(1, 3);
var BackChess3 := ChessFactory.GetChess(Tcolor.BLACK);
BackChess3.Draw(1, 3);
//打印一下内存地址
writeln(Integer(WhiteChess1),',',Integer(BackChess1), ',', Integer(BackChess2), ',', Integer(BackChess3));
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
Readln;
end.
核心代码
unit UnitFlyweight;
interface
uses
System.Generics.Collections;
type
//定义一个枚举类型
TColor = (BLACK, WHITE);
type
//定义一个共享对象通用的接口
IChess = interface
//绘制棋子
procedure Draw(x, y: Integer);
end;
type
//黑棋类
TBlackChess = class(TInterfacedObject, IChess)
private
//内部状态,共享
FColor: TColor;
const
SHARP: string = '圆形';
public
//重写接口中的函数
procedure Draw(x, y: Integer); overload;
//重写构造
constructor Create(); overload;
end;
type
//白棋类
TWhiteChess = class(TInterfacedObject, IChess)
private
//内部状态,共享
FColor: TColor;
const
SHARP: string = '圆形';
public
//重写接口中的函数
procedure Draw(x, y: Integer); overload;
//重写构造
constructor Create(); overload;
end;
type
//共享对象工厂
TChessFactory = class
private
FDictionary: TDictionary<TColor, IChess>;
public
constructor Create(); overload;
function GetChess(Color: TColor): IChess;
end;
implementation
uses
System.SysUtils, System.TypInfo;
{ TBlackChess }
constructor TBlackChess.Create();
begin
inherited;
//颜色赋值
FColor := TColor.BLACK;
end;
procedure TBlackChess.Draw(x, y: Integer);
begin
var EnumName := GetEnumName(TypeInfo(TColor), 0);
Writeln(SHARP + EnumName + '棋子置于(' + inttostr(x) + ',' + inttostr(y) + ')处')
end;
{ TWhiteChess }
constructor TWhiteChess.Create;
begin
inherited;
//颜色赋值
FColor := TColor.WHITE;
end;
procedure TWhiteChess.Draw(x, y: Integer);
begin
var EnumName := GetEnumName(TypeInfo(TColor), 1);
Writeln(SHARP + EnumName + '棋子置于(' + inttostr(x) + ',' + inttostr(y) + ')处')
end;
{ TChessFactory }
constructor TChessFactory.Create;
begin
FDictionary := TDictionary<TColor, IChess>.Create;
end;
function TChessFactory.GetChess(Color: TColor): IChess;
begin
var Chess: IChess;
FDictionary.TryGetValue(Color, Chess);
if Chess = nil then
begin
if Color = TColor.WHITE then
Chess := TWhiteChess.Create
else
Chess := TBlackChess.Create;
FDictionary.Add(Color, Chess);
end;
Result := Chess;
end;
end.
执行结果
从调用的情况看,下了 3 次黑棋,而每一次黑棋的内存地址都是相同的,说明并没有创建新的对象
最后说一下设计模式是 1995 年,GoF(Gang of Four,四人组/四人帮)合作出版了《设计模式:可复用面向对象软件的基础》一书,共收录了 23 种设计模式,从此树立了软件设计模式领域的里程碑,人称「GoF 设计模式」
至此设计模式部分就彻底的完结了,虽然很多人不喜欢这个系列。接下来我暂时不知道该更新什么系列内容了,暂时定为网络编程吧!