技术演进中的开发沉思-66 DELPHI VCL系列:VCL 永续储存的设计智慧

前面花了三章篇幅说了VCL下的永续存储,有朋友说,觉得还缺乏一个在VCL大框架下的总结。既然要站在框架这个绝度,那么VCL 的永续储存不是简单的 "保存 - 读取",而需要理解是一套精心编排的设计模式舞蹈。那些看似零散的类和方法,实则遵循着严谨的设计逻辑,就像老式钟表里精准咬合的齿轮。

一、 VCL 永续储存的设计模式

设计模式这东西,年轻时总觉得是理论家的噱头,直到在项目中踩了足够多的坑才明白 —— 它们是前辈们用血泪总结的 "避坑指南"。VCL 的永续储存机制里,藏着好几段精妙的设计模式 "独舞"。

1、Two-Way Sequential

第一次理解 Two-Way Sequential 模式,是在和公司专门负责维修线路的老电工聊天。他说:"好的电路必须能送电也能断电,还得知道现在是送电还是断电。" 这话让我想到 ——VCL 的流操作不就是这样吗?既能写入数据(送电),又能读取数据(断电),还通过Position属性记录当前位置(知道状态)。

这种双向顺序模式就像两个人对话:你一句(写),我一句(读),必须按顺序来,还得记得刚才说到哪了。TReader 和 TWriter 正是这种模式的完美实现,它们一个负责说,一个负责听,配合默契。

// Two-Way Sequential模式的简单实现
procedure TwoWaySequentialDemo;
var
  Stream: TMemoryStream;
  Writer: TWriter;
  Reader: TReader;
  name: string;
  age: Integer;
begin
  Stream := TMemoryStream.Create;
  Writer := TWriter.Create(Stream, 1024);
  Reader := TReader.Create(Stream, 1024);
  try
    // 写入数据(发送消息)
    Writer.WriteString('老程序员');
    Writer.WriteInteger(45);
    Writer.Flush;
    
    // 重置位置(准备倾听)
    Stream.Position := 0;
    
    // 读取数据(接收消息)
    name := Reader.ReadString;
    age := Reader.ReadInteger;
    
    ShowMessageFmt('姓名:%s,年龄:%d', [name, age]);
  finally
    Reader.Free;
    Writer.Free;
    Stream.Free;
  end;
end;

2、Adapter

年轻时层做一个跨系统数据同步项目时,遇到个头疼问题:我们的设备用自定义协议输出数据,而合作方的系统只认标准 XML。最后了解到设计模式,是 Adapter 模式救了场 —— 就像旅行时带的万能电源转换器,让两种不兼容的接口能顺畅对接。

VCL 的永续储存里,Adapter 模式无处不在。当一个组件的存储格式和流系统不兼容时,就需要一个 "转换器" 来协调。Delphi 提供了多种 Adapter 实现方式,各有各的妙用。

接口 - 类别混合的 Adapter:像带多接口的转换器,既懂组件的 "语言",又懂流的 "规则"。

// 接口-类别混合的Adapter示例
type
  // 组件的接口(设备插头)
  IMyComponent = interface
    ['{GUID}']
    function GetData: string;
    procedure SetData(const Value: string);
  end;

  // 流的接口(电源插座)
  IStreamAdapter = interface
    ['{GUID}']
    procedure SaveToStream(Stream: TStream);
    procedure LoadFromStream(Stream: TStream);
  end;

  // Adapter(转换器)
  TComponentStreamAdapter = class(TInterfacedObject, IStreamAdapter)
  private
    FComponent: IMyComponent;
  public
    constructor Create(AComponent: IMyComponent);
    procedure SaveToStream(Stream: TStream);
    procedure LoadFromStream(Stream: TStream);
  end;

constructor TComponentStreamAdapter.Create(AComponent: IMyComponent);
begin
  inherited Create;
  FComponent := AComponent;
end;

procedure TComponentStreamAdapter.SaveToStream(Stream: TStream);
var
  Writer: TWriter;
begin
  Writer := TWriter.Create(Stream, 1024);
  try
    // 把组件数据转换成流能理解的格式
    Writer.WriteString(FComponent.GetData);
  finally
    Writer.Free;
  end;
end;

procedure TComponentStreamAdapter.LoadFromStream(Stream: TStream);
var
  Reader: TReader;
begin
  Reader := TReader.Create(Stream, 1024);
  try
    // 把流数据转换成组件能理解的格式
    FComponent.SetData(Reader.ReadString);
  finally
    Reader.Free;
  end;
end;

类别继承的 Adapter:更像定制化的转换器,通过继承获得基础能力,再通过重写适配特定需求。比如 TComponent 就是通过继承 TPersistent,获得了基础的存储能力,再通过重写方法适配组件特有的存储需求。

// 类别继承的Adapter示例
type
  // 基础持久化类(标准插座)
  TBasePersistence = class(TPersistent)
  public
    procedure Save(Stream: TStream); virtual;
    procedure Load(Stream: TStream); virtual;
  end;

  // 自定义组件(特殊插头)
  TMySpecialComponent = class(TBasePersistence)
  private
    FSpecialData: Integer;
  public
    property SpecialData: Integer read FSpecialData write FSpecialData;
    // 重写方法实现适配
    procedure Save(Stream: TStream); override;
    procedure Load(Stream: TStream); override;
  end;

procedure TBasePersistence.Save(Stream: TStream);
begin
  // 基础存储逻辑
end;

procedure TBasePersistence.Load(Stream: TStream);
begin
  // 基础加载逻辑
end;

procedure TMySpecialComponent.Save(Stream: TStream);
var
  Writer: TWriter;
begin
  inherited; // 先执行基础逻辑
  Writer := TWriter.Create(Stream, 1024);
  try
    // 适配特殊数据的存储
    Writer.WriteInteger(FSpecialData);
  finally
    Writer.Free;
  end;
end;

procedure TMySpecialComponent.Load(Stream: TStream);
var
  Reader: TReader;
begin
  inherited; // 先执行基础逻辑
  Reader := TReader.Create(Stream, 1024);
  try
    // 适配特殊数据的加载
    FSpecialData := Reader.ReadInteger;
  finally
    Reader.Free;
  end;
end;

3、设计模式的合奏

记得有个项目需要保存用户绘制的工艺流程图 —— 里面有各种形状、线条、文字标注,每种元素都有自己的属性。我们用 Two-Way Sequential 模式保证读写顺序,用 Adapter 模式让每种图形元素都能和流系统对话,最终实现了 "绘制 - 保存 - 再编辑" 的完整闭环。

这些模式单独看可能平平无奇,但组合起来就像一支精密的乐队,每个乐器(模式)都在恰当的位置发挥作用,共同奏响了 "永续储存" 这首曲子。

二、串行流类别

如果说设计模式是流操作的 "章法",那串行流类别就是承载数据的 "容器"。Delphi 提供了一系列流类,就像生活中不同的容器 —— 有的适合临时存放(如内存流),有的适合长期保存(如文件流),有的适合网络传输(如缓冲流)。

1、TStream

TStream 是所有流类的基类,就像所有容器都有的基本属性 —— 能装东西,能知道装了多少,能移动到不同位置取东西。它定义了ReadWriteSeek等基础方法,不管哪种流,都得遵守这些基本规则。

2、TMemoryStream

做实时数据处理时,TMemoryStream 是我的最爱。它把数据存在内存里,读写速度飞快,就像把临时想法写在便签上,方便随时修改。但缺点也和便签一样 —— 程序关掉就没了。

// 用TMemoryStream处理临时数据
procedure MemoryStreamDemo;
var
  MemStream: TMemoryStream;
  Data: array[0..9] of Byte;
  i: Integer;
  ReadData: array[0..9] of Byte;
begin
  MemStream := TMemoryStream.Create;
  try
    // 准备数据
    for i := 0 to 9 do
      Data[i] := i;
      
    // 写入内存流
    MemStream.Write(Data, SizeOf(Data));
    
    // 读取数据
    MemStream.Position := 0;
    MemStream.Read(ReadData, SizeOf(ReadData));
    
    // 数据处理...
  finally
    MemStream.Free; // 释放时数据就消失了
  end;
end;

3、TFileStream

需要长期保存数据时,TFileStream 就派上用场了。它把数据存在硬盘上,就像把重要文件放进档案盒,关机也不会丢失。当年做配置保存功能,就是用它把用户设置稳稳当当地存在config.dat里。

// 用TFileStream保存配置
procedure SaveConfig;
var
  FileStream: TFileStream;
  Config: string;
begin
  Config := 'WindowPos=100,100,800,600'#13#10'FontSize=12';
  
  // 创建或打开文件
  if FileExists('config.dat') then
    FileStream := TFileStream.Create('config.dat', fmOpenWrite)
  else
    FileStream := TFileStream.Create('config.dat', fmCreate);
  
  try
    // 写入配置
    FileStream.WriteBuffer(Config[1], Length(Config));
  finally
    FileStream.Free;
  end;
end;

4、其他实用流类

  • TStringStream:专门处理字符串的 "文字袋",省去了字符串和字节数组的转换麻烦。
  • TBufferedStream:带缓冲的 "快递箱",通过先缓存再批量读写,提高文件或网络操作效率。
  • TCompressionStream/TDecompressionStream:会 "压缩" 的容器,像真空袋一样减少数据体积,适合传输和存储大文件。

当年做一个日志分析工具,需要处理几百 MB 的日志文件,正是用了 TBufferedStream,让读取速度提升了近十倍。这让我明白:选对合适的流类,就像用对了工具,能事半功倍。

最后小结:

回望这些流类和设计模式,忽然发现 VCL 的永续储存机制藏着一种朴素的智慧:它不追求极致的精巧,而是通过分层设计(基础流类→设计模式→组件实现),让复杂问题变得可控。就像老木匠做柜子,先做好标准的木板(流类),再设计好连接方式(模式),最后根据需求组合出不同的柜子(组件)。这种思想在今天的微服务、组件化开发中依然闪耀 —— 技术在变,但解决问题的底层逻辑,往往是相通的。是它们让我明白:好的技术不是炫技,而是像水一样,无形却能适应各种容器,默默解决问题。这或许就是 VCL 永续储存机制留给我们的,超越代码本身的启示。未完待续....

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值