字符串到 Class:转换 Class 或基本转换方法?

String to Class: Conversion Class or Base Conversion Method?

我正在做一个项目,我在其中构建了一个由基本 class "TFuncao"、两个后代 classes "TSolicitacao"、[=38= 组成的结构] 和尽可能多的后代 classes,所有这些都是 class-like-models only with properties.

第一个是这个问题的重点,它有一个方法"ParaTexto",它returns一个字符串的所有属性以特定的方式(基本上将它们转换为字符串)。 "TSolicitacao" class 有很多后代,每个后代都有方法 "ParaTexto" 调用其祖先函数并添加自己的属性。

那么,我的问题是:

我应该保留这个方法吗"ParaTexto"?或者删除它并使用接收 "TSolicitacao" 的方法创建一个 class 并相应地根据其类型(后代 class) returns 一个字符串?

我想到这个是因为模型 class 在技术上应该只有 getter 和 setter。

代码 - 移除以保存的 getter 和 setter space:

"TFuncao" class 文件:

unit Funcao;

interface

uses
  Funcao.Tipo;

type
  TFuncao = class abstract
  private
    FFUNCAO: TFuncaoTipo;
    FSEQUENC: string;
    procedure SetFUNCAO(const Value: TFuncaoTipo);
    procedure SetSEQUENC(const Value: string);
  published
    property SEQUENC: string read FSEQUENC write SetSEQUENC;
    property FUNCAO: TFuncaoTipo read FFUNCAO write SetFUNCAO;
    constructor Create; virtual;
  end;

"TSolicitacao" class 文件:

unit Funcao.Solicitacao;

interface

uses
  Funcao;

type
  TSolicitacao = class abstract (TFuncao)
  published
    function ParaTexto: string; virtual;
  end;

implementation

uses
  Funcoes,
  SysUtils;

{ TSolicitacao }

function TSolicitacao.ParaTexto: string;
begin
  Result :=
    Copy(CompletarComEspacoAEsquerda(SEQUENC, 4), 1, 4) +
    Completar0AEsquerda(IntToStr(Integer(FUNCAO)), 2);
end;

end.

"TVendaSolicitacao" class 文件 - "TSolicitacao":

的后代 class 示例
unit Venda.Solicitacao;

interface

uses
  Funcao.Solicitacao,
  Funcao.Tipo,
  Venda.Solicitacao.Produto;

type
  TVendaSolicitacao = class (TSolicitacao)
  private
    FNUMFISCAL: Integer;
    FNUMPDV: Integer;
    FAUTORIZ: string;
    FCONV_TIPO: Integer;
    FProdutos: TVendaSolicitacaoProdutoList;
    procedure SetAUTORIZ(const Value: string);
    procedure SetCONV_TIPO(const Value: Integer);
    procedure SetNUMFISCAL(const Value: Integer);
    procedure SetNUMPDV(const Value: Integer);
    procedure SetProdutos(const Value: TVendaSolicitacaoProdutoList);
  published
    property NUMPDV: Integer read FNUMPDV write SetNUMPDV;
    property NUMFISCAL: Integer read FNUMFISCAL write SetNUMFISCAL;
    property AUTORIZ: string read FAUTORIZ write SetAUTORIZ;
    property CONV_TIPO: Integer read FCONV_TIPO write SetCONV_TIPO;
    property Produtos: TVendaSolicitacaoProdutoList read FProdutos write SetProdutos;
    function ParaTexto: string; override;
    constructor Create; override;
  end;

implementation

uses
  SysUtils,
  Funcoes;

{ TVendaSolicitacao }

constructor TVendaSolicitacao.Create;
begin
  inherited;
  FUNCAO := ftVenda;
  FProdutos := TVendaSolicitacaoProdutoList.Create;
end;

function TVendaSolicitacao.ParaTexto: string;
begin
  Result :=
    inherited +
    Completar0AEsquerda(NUMPDV, 4) +
    Completar0AEsquerda(NUMFISCAL, 6) +
    Completar0AEsquerda(AUTORIZ, 12) +
    IntToStr(CONV_TIPO);

  if Produtos.Count > 0 then
    Result := Result + #13#10 + Produtos.ParaTexto;
end;

end.

"TConversao" class 文件 - 如我所见:

unit Conversao;

interface

uses
  Funcao;

type
  TConversao = class
  published
    function ParaTexto(Funcao: TFuncao): string;
  end;

implementation

uses
  Solicitacao,
  Solicitacao.Venda,
  Funcoes;

function ParaTexto(Funcao: TFuncao): string;
begin
  Result := '';

  if (Funcao is TSolicitacao) then
    Result :=
      Copy(CompletarComEspacoAEsquerda((Funcao as TSolicitacao).SEQUENC, 4), 1, 4) +
      Completar0AEsquerda(IntToStr(Integer((Funcao as TSolicitacao).FUNCAO)), 2);

  if (Funcao is TVendaSolicitacao) then
  begin
    Result :=
      inherited +
      Completar0AEsquerda((Funcao as TVendaSolicitacao).NUMPDV, 4) +
      Completar0AEsquerda((Funcao as TVendaSolicitacao).NUMFISCAL, 6) +
      Completar0AEsquerda((Funcao as TVendaSolicitacao).AUTORIZ, 12) +
      IntToStr((Funcao as TVendaSolicitacao).CONV_TIPO);

    if Produtos.Count > 0 then
      Result := Result + #13#10 + Produtos.ParaTexto;
  end;

是的,你应该保留这个方法,这样代码的可读性和可重用性要高得多。比如说,您定义了 TSolicitacao 的新后代,您覆盖了 ParaTexto,仅此而已。所有更改都在一处 - 在您的 class 定义中。

您也可以尝试使用 RTTI 遍历 class 的所有属性并自动生成字符串。

更新

一些提示可以如何使用 RTTI(工作示例)。您定义不同类型的整数:

  T7digitsInt = type Int64;
  T8digitsInt = type Int64;

此外,从 Int64 到字符串的函数类型:

TConvertCustomIntToStringProc = function (num: Int64): string;

一些具有属性的测试 class,类似于您的 TSolicitacao:

  TEntry = class (TObject)
    private
      fPlainInt: Int64;
      f7digitInt: T7digitsInt;
      fWriteOnlyInt: T7digitsInt;
      f8digitInt: T8digitsInt;
    published
      property PlainInt: Int64 read fPlainInt write fPlainInt;
      property NotSoPlainInt: T7digitsInt read f7digitInt write f7digitInt;
      property AnotherInt: T7digitsInt write fWriteOnlyInt;
      property Biggest: T8digitsInt read f8digitInt write f8digitInt;
  end;

此外,我们在我们的单元中声明了全局变量,这是不同 属性 类型的列表:

var PropertyConverters: TDictionary<string, TConvertCustomIntToStringProc>;

现在,在实现部分我们定义了一些将整数转换为字符串的函数:

function ShowPlainInt64(num: Int64): string;
begin
    Result:=IntToStr(num);
end;

function Show7digitsInt(num: Int64): string;
begin
    Result:=Format('%.7d',[num]);
end;

我们'register'这个函数分别处理Int64和T7DigitsInt,在初始化的某处,例如TForm1.create:

procedure TForm1.FormCreate(Sender: TObject);
begin
  PropertyConverters:=TDictionary<string,  ConvertCustomIntToStringProc>.Create;
  PropertyConverters.Add('Int64',ShowPlainInt64);
  PropertyConverters.Add('T7digitsInt',Show7DigitsInt);
end;

在 TForm1.FormShow 中,我们创建了 TEntry 的实例并用值填充它以测试我们的转换器,然后尝试访问它们,遍历属性:

procedure TForm1.FormShow(Sender: TObject);
var entry: TEntry;
    ctx : TRttiContext;
    rt : TRttiType;
    prop : TRttiProperty;
    convertProc: TConvertCustomIntToStringProc;
begin
  entry:=TEntry.Create;
  entry.PlainInt:=12;
  entry.NotSoPlainInt:=34;

  //iterating through all the properties with RTTI
  ctx := TRttiContext.Create();
    try
        rt := ctx.GetType(entry.ClassType);

        for prop in rt.GetProperties() do begin
          if prop.IsReadable then
            if PropertyConverters.TryGetValue(prop.PropertyType.Name,convertProc) then
              Memo1.Lines.Add(prop.Name+'='+convertProc(prop.GetValue(entry).AsOrdinal))
            else
              Memo1.lines.Add(prop.Name+':unregistered type')
          else
            Memo1.Lines.Add(prop.Name+':write-only');



        end;
    finally
        ctx.Free();
    end;

end;

这就是我得到的:

PlainInt=12
NotSoPlainInt=0000034
AnotherInt:write-only
Biggest:unregistered type

如你所见,我们有几个整数,它们只是类型不同,因此我们让它们显示不同。如果没有那么多类型,那么这样的代码可能会非常高效。