Torne o código Delphi mais legível usando os ajudantes de classe:
Chamada clássica:
StoreDataset ( 1 , mysqlDataSet, true);Chamada mais legível:
mysqlDataSet.StoreSelected_StopAfterFirstError(
function():boolean
begin
Result := mysqlDataSet.FieldByName( ' Status ' ). Value = ' 1 '
end ), fDataStorer);Helper tbytes - gerar números, compressa zlib, codificação base64, calc crc32
uses
Helpers.TBytes;
type
TUploadImageCommand = record
Image: string;
ControlSum: integer;
end ;
procedure SendCommandToRestServer ( const aCommand: TUploadImageCommand);
begin
writeln( ' Use RestClient or IdHttpClient to send POST request ' );
writeln( ' POST /rest/upload-image/ TUploadImageCommand ' );
writeln( ' TUploadImageCommand: ' );
writeln( ' ControlSum = ' , aCommand.ControlSum);
writeln( ' Image Length = ' , Length(aCommand.Image));
writeln( ' Image = ' , aCommand.Image);
end ;
var
bytes: TBytes;
idx: integer;
memoryStream: TMemoryStream;
command := TUploadImageCommand;
begin
bytes.Size := 1000 ;
for idx := 0 to bytes.Size- 1 do
bytes[idx] := idx div 10 ;
memoryStream := TMemoryStream.Create();
bytes := CompressToStream(memoryStream);
command.Image := bytes.GenerateBase64Code();
command.ControlSum := bytes.GetSectorCRC32( 0 , bytes.Size);
SendCommandToRestServer(command);
end .Helper TBytes : Armazene e carregue os tbytes do fluxo ou arquivo. Carregar e verificar a imagem PNG
uses
Helpers.TBytes;
var
bytes: TBytes;
idx: integer;
memoryStream: TMemoryStream;
command := TUploadImageCommand;
begin
bytes.InitialiseFromBase64String( ' U2FtcGxlIHRleHQ= ' );
bytes.SaveToFile( ' notes.txt ' ); // save: Sample text
memoryStream:= bytes.CreateStream();
// memoryStream.Size = 11
memoryStream.Free;
// -----------------
s := bytes.GetSectorAsString( 0 , 6 ); // ASCII only text
bytes := [ 0 , 0 , 15 , 16 , $A0, 255 , 0 , 0 , 0 , 0 , 1 ];
if bytes.GetSectorAsHex( 2 , 4 ) = ' 0F 10 A0 FF ' then
begin
memoryStream := TMemoryStream.Create();
memoryStream.LoadFromFile( ' small.png ' );
memoryStream.Position := 0 ;
signature.LoadFromStream(memoryStream, 8 );
if (signature.GetSectorAsHex = ' 89 50 4E 47 0D 0A 1A 0A ' ) and
(signature.GetSectorAsString( 1 , 3 ) = ' PNG ' ) then
begin
memoryStream.Position := 0 ;
pngImage := TPngImage.Create;
pngImage.LoadFromStream(memoryStream);
// Image1.Picture := pngImage;
pngImage.Free;
end ;
memoryStream.Free;
end ;
end ;TDATETIME Helper : Informações sobre TDATETIME
uses
Helpers.TDateTime;
var
date: TDateTime;
begin
date := EncodeDate( 1989 , 06 , 04 );
writeln(date.AsYear); // 1989
writeln(date.AsMonth); // 06
writeln(date); // 06/04/1989
writeln(EncodeDate( 2017 , 10 , 24 ).DayOfWeek); // 3
writeln(date.IncMonth( 5 ).ToString( ' yyyy-mm-dd ' ); // 1989-11-04
writeln(date.AsStringDateISO); // 1989-06-04
date := EncodeDate( 2019 , 10 , 24 ) + EncodeTime( 18 , 45 , 12 , 0 );
writeln(date.AsStringDateISO); // 2019-10-24T18:45:12.000Z
end .Helper tdataset : foreachrow, loaddata <>, savedata <>
uses
Helpers.TDataSet;
type
TCity = class
public
id: Integer;
City: string;
Rank: Variant;
visited: Variant;
end ;
var
dataset: TDataSet;
cityNames: TArray<string>;
idx: integer;
cities: TObjectList<TCityForDataset>;
begin
dataset := GivenDataSet(fOwner, [
{ } [ 1 , ' Edinburgh ' , 5.5 , EncodeDate( 2018 , 05 , 28 )],
{ } [ 2 , ' Glassgow ' , 4.5 , EncodeDate( 2015 , 09 , 13 )],
{ } [ 3 , ' Cracow ' , 6.0 , EncodeDate( 2019 , 01 , 01 )],
{ } [ 4 , ' Prague ' , 4.9 , EncodeDate( 2013 , 06 , 21 )]]);
SetLength(cityNames, dataset.RecordCount);
idx := 0 ;
dataset.ForEachRow(
procedure
begin
cityNames[idx] := dataset.FieldByName( ' city ' ).AsString;
inc(idx);
end );
writeln(string.Join( ' , ' , citiecityNamess));
cities := dataset.LoadData<TCityForDataset>();
witeln(cities.Count); // 4
witeln(cities[ 0 ].City); // Edinburgh
witeln(cities[ 3 ].Rank); // 4.9
cities[ 2 ].Rank := 5.8 ;
cities[ 2 ].visited := EncodeDate( 2020 , 7 , 22 );
cities.Add(TCity.Create());
cities[ 4 ].id := 5 ;
cities[ 4 ].City := ' Warsaw ' ;
dataset.SaveData<TCity>(cities);
// SaveData updated Cracow record and added Warsaw
endTStringGrid Helper : Encha e redimensione TStringGrid
// StringGrid1: TStringGrid;
// StringGrid2: TStringGrid;
procedure TForm1.Button1Click (Sender: TObject);
var
structure, rows: string;
begin
StringGrid1.ColCount := 4 ;
StringGrid1.RowCount := 3 ;
StringGrid1.ColsWidth([ 40 , 100 , 90 , 110 , 80 ]);
StringGrid1.FillCells([
[ ' 1 ' , ' Jonh Black ' , ' U21 ' , ' 34 ' ],
[ ' 2 ' , ' Bogdan Polak ' , ' N47 ' , ' 28 ' ]]);
structure :=
' {"column": "no", "caption": "No.", "width": 30}, ' +
' {"column": "mesure", "caption": "Mesure description", "width": 200}, ' +
' {"column": "value", "caption": "Value", "width": 60} ' ;
rows :=
' {"no": 1, "mesure": "Number of DI Containers", "value": 120}, ' +
' {"no": 2, "mesure": "Maximum ctor injection", "value": 56} ' ;
data
jsData := TJSONObject.ParseJSONValue(Format(
' {"structure": [%s], "data": [%s]} ' , [structure, rows])
) as TJSONObject;
StringGrid2.FillWithJson(jsData);
end ;Ajudantes RTL:
| Unidade | Descrição do ajudante |
|---|---|
| Helper.tbytes | Permite manipular matrizes de bytes: tamanho, carga e salvar, getter e setters |
| Helper.tdataset | Funcionalidade adicional do TDataSet como: iterando através do conjunto de dados ou loadData / savedata - permite mapear uma lista de objetos para o conjunto de dados |
| Helper.TDATETIME | Métodos que permitem manipular facilmente a data e a hora |
| Helper.tfield | Permite carregar dados base64 no campo BLOB ou verificar a assinatura dos dados armazenados |
| Helper.tjsonObject | Métodos lendo dados ou armazenamento na estrutura JSON DOM, como IsValidisodate (nome do campo) |
| Helper.TStream | Métodos que facilitam a leitura e a escrita de dados para fluxos |
Ajudantes de VCL:
| Classe expandida | Descrição do ajudante |
|---|---|
| Tapplication | Amostra auxiliar contendo método experimental como: InDeveloperMode . |
| TdbGrid | Métodos Manipulando colunas DBGrid, como: AUTOSIZECOLUNS - OBRIGADO automaticamente com de cada coluna |
| Tform | Métodos de gerenciamento de temporizadores: setInterval e Settimeout |
| Tpicture | Deixe atribuir tbytes e tblobfield para tpicture com reconhecimento automático de formato de imagem |
| TStringGrid | Preencher e configurar o controle da grade de cordas: carregando dados, definindo largura das colunas, limpeza do conteúdo da célula ou linha |
| Twincontrol | Métodos de utilidade para pesquisar controles infantis por tipo ou por nome. Visível para todos os descendentes de Twincontrol: tform, tpanel, etc. |
Outros ajudantes:
| Classe expandida | Descrição do ajudante |
|---|---|
| Helper.tfdConnection | |
| Helper.tfdcustomManager |
A Convenção de Nomeação do Helper é adicionar Helper de sufixo ao nome da classe expandida, o que significa que o ajudante de classe para TDataSet terá um nome TDataSetHelper .
Cada ajudante é armazenado em um arquivo e unidade separados, seu nome é Helper.<ExpanedClassName>.pas .
Todas as unidades auxiliares são armazenadas na subpasta src - vá para esse local.
examples/01-playground/ - Vá para esse localHelperPlayground.dprHelper.TStringGrid.pasHelper.TDataSet.pas e Helper.TDBGrid.pasHelper.TBytes.pas e Helper.TStream.pasexamples/02-formhelper/ - Vá para esse localHelpersMiniDemo.dprHelper.TForm.pas e Uso do Timer A Métodos HelperA enorme quantidade de código VCL (FMX) pode ser limpa usando ajudantes de classe, que são realmente uma técnica de refatoração fácil, com baixo risco de projetos complexos. Usando esse método, as equipes podem iniciar a atualização de seus projetos legados, mesmo sem a rede de segurança dos testes de unidade. Além disso, a verificação de ajudantes recém -criados pode ser feita facilmente com os testes de unidade. Essa abordagem permite ensinar aos desenvolvedores como escrever testes de unidade de maneira correta (aprenda na prática os primeiros princípios ou outros). As equipes também podem aplicar facilmente o processo de desenvolvimento do TDD (escreva os testes primeiro e depois implemente a funcionalidade) de maneira divertida e não invasiva.
Às vezes, os ajudantes de classe também podem ser perigosos se forem usados de forma inadequada. Por esse motivo, é necessário aplicar um pouco mais de processo de desenvolvimento e entrega disciplinado, as sugestões relacionadas a essa área são abordadas nas seções a seguir.
Benefícios dos ajudantes de classe:
Desde o início (Delphi 2006) até a versão Delphi Berlin / 10.1, havia um bug de classe de classe bastante popular, que permite acessar campos privados e métodos privados usando ajudantes. Devido a esse erro, muitos desenvolvedores identificaram essa extensão interessante de linguagem com esse hack. O uso indevido dos ajudantes de classe causou esse valor dessa solução super poderosa é subestimada.
Um dos propósitos importantes do uso de ajudantes de classe é a capacidade de extrair código útil e reutilizável e, em seguida, cubra -os com testes de unidade. Os desenvolvedores podem até empregar facilmente TDD, abordagem orientada por teste na qual primeiro precisamos escrever testes de unidade e depois implementar a lógica
Esse repositório está demonstrando como praticar a abordagem do TDD. Cada classe e auxiliar de gravação tem teste Dunitx. Os conjuntos de testes de unidade podem ser facilmente expandidos para fornecer melhor cobertura de teste. Para ter uma melhor experiência em testes de unidade, é recomendável instalar o melhor TestInsight de Extensão do TDD Delphi IDE - gratuito e uma plataforma muito produtiva criada por Stefan Glienke. Glória para o autor! Link para o repositório do testInsight: vá para o site do Bitbucket
O teste de unidade de amostra pode ser encontrado na pasta de repositório tests - vá para esse local
Teste de amostra do método ColsWidth de classe TStringGrid :
procedure TestTStringGridHelper.FiveColumns_ColsWidth ;
begin
fGrid.ColCount := 5 ;
fGrid.ColsWidth([ 50 , 100 , 90 , 110 , 80 ]);
Assert.AreEqual( 110 , fGrid.ColWidths[ 3 ]);
Assert.AreEqual( 80 , fGrid.ColWidths[ 4 ]);
end ;Os ajudantes de classe parecem realmente promissores no imploramento e, na verdade, existem ótimas solução, mas ao criar e usar cada vez mais, começará a notar alguns obstáculos. Por esse motivo, boas práticas devem ser adaptadas desde o início para ajudar a evitar possíveis problemas.
Uma das práticas recomendadas ao usar ajudantes de classe é planejar uma boa manutenção do projeto, incluindo controle de versão e gerenciamento de liberação. Etapas comprovadas, incluindo dois pontos importantes:
Este projeto do GitHub é um exemplo ao vivo de tais técnicas de implantação. Estamos usando o modelo de ramificação inspirado no Vincent Driessen Blog Post: Um modelo de ramificação Git bem -sucedido, juntamente com o modelo de planejamento e entrega, inspirado no método Kanban.
Ajudantes de classe Project Modelo de ramificação

is021-grid-column-restore é para o novo recurso: Método LoadColumnsFromJsonString no auxiliar de classe TDBGrid, que permite restaurar a configuração da coluna (ordem, legenda do título, largura e visibilidade) armazenadas na sequência JSON. A definição de recurso está escrita no GitHub Issue #21is014-doc-dark-side é uma nova seção de documentação no arquivo principal README.md .Ajudantes de classe Projeto Kanban Board

As sessões de diretoria e planejamento da Kanban são técnicas para alcançar - entrega incremental. O projeto Helpers de classe não pode ser entregue com muita frequência, devido ao custo de integração (repositório de auxiliar de classe de integração com projetos finais da Delphi). E da outra entrega lateral da nova versão não deve demorar muito, porque todos os projetos devem usar vantagens de novos ajudantes (alta reutilização).
Os ajudantes de classe parecem muito bons no primeiro contato, mas eles têm alguns efeitos colaterais perigosos. Nesta seção, você pode entender melhor as fraquezas desta solução. Se você tentar definir dois ajudantes de classe expandindo a mesma classe base, verá que apenas um deles será visível. Mais para isso, você não consegue expandir a funcionalidade do ajudante de classe com herança. Além disso, você não pode definir a memória adicional (campos) no auxiliar de classe.
Você pode proteger seu projeto contra os efeitos dessas fraquezas. Antes de definir um novo ajudante de classe, você deve fazer algumas perguntas:
TButton ), não para mais para mais geral ( TControl , TComponent , etc.).