字符串到 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
如你所见,我们有几个整数,它们只是类型不同,因此我们让它们显示不同。如果没有那么多类型,那么这样的代码可能会非常高效。
我正在做一个项目,我在其中构建了一个由基本 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
如你所见,我们有几个整数,它们只是类型不同,因此我们让它们显示不同。如果没有那么多类型,那么这样的代码可能会非常高效。