Mudanças entre as edições de "Tiny Types"
De Grupo Acert
(Criou página com ''''Tiny Types''' é um padrão que basicamente propõe encapsular tipos simples dentro classes específicas de domínio. Parece simples, mas o uso de Tiny Types, além de prop...') |
|||
| Linha 3: | Linha 3: | ||
Vamos usar como exemplo códigos fictícios, porém muito comum de encontrar em diversos projetos: | Vamos usar como exemplo códigos fictícios, porém muito comum de encontrar em diversos projetos: | ||
| − | TPessoa = class | + | TPessoa = class |
| − | public | + | public |
| − | + | property Nome: string read FNome write FNome; | |
| − | + | property CNPJ: string read FCNPJ write FCNPJ; | |
| − | + | // ... | |
| − | end; | + | end; |
Com essa classe em mãos conseguimos usar o código normalmente. | Com essa classe em mãos conseguimos usar o código normalmente. | ||
| − | + | var | |
| − | var | + | Pessoa: TPessoa; |
| − | + | begin | |
| − | begin | + | // ... |
| − | + | Pessoa.Nome := 'Tecsystem'; | |
| − | + | Pessoa.CNPJ := '99.999.999/0001-91'; | |
| − | + | end; | |
| − | end; | + | |
O problema aparece quando queremos adicionar mais utilidade ao codigo, como por exemplo validação, formatação, entre várias outras possibilidades. Nesse caso poderiamos incluir essas utilidades na classe TPessoa, o que tornaria difícil do reuso do código. | O problema aparece quando queremos adicionar mais utilidade ao codigo, como por exemplo validação, formatação, entre várias outras possibilidades. Nesse caso poderiamos incluir essas utilidades na classe TPessoa, o que tornaria difícil do reuso do código. | ||
| Linha 25: | Linha 24: | ||
Uma alternativa, que é a mais usada, seria uma classe utilitária onde teremos diversas funções para resolver esses pequenos problemas. | Uma alternativa, que é a mais usada, seria uma classe utilitária onde teremos diversas funções para resolver esses pequenos problemas. | ||
| − | + | TClasseUtils = class | |
| − | TClasseUtils = class | + | public |
| − | public | + | class function ValidaCNPJ(const CNPJ: string): string; |
| − | + | class function GetCNPJFormatado(const CNPJ: string): string; | |
| − | + | end; | |
| − | end; | + | |
Dessa forma podemos validar o CNPJ em qualquer lugar do sistema, porém criamos dependência dessa TClasseUtils. | Dessa forma podemos validar o CNPJ em qualquer lugar do sistema, porém criamos dependência dessa TClasseUtils. | ||
| − | + | if TClasseUtils.ValidaCNPJ(Pessoa.CNPJ) then | |
| − | if TClasseUtils.ValidaCNPJ(Pessoa.CNPJ) then | + | // ... |
| − | + | ||
Usando o Tiny Types | Usando o Tiny Types | ||
| Linha 43: | Linha 40: | ||
Uma outra alternativa, que na minha opinião deixa o código mais elegante e expressivo, é através do uso de Tiny Types. Então, que tal isso: | Uma outra alternativa, que na minha opinião deixa o código mais elegante e expressivo, é através do uso de Tiny Types. Então, que tal isso: | ||
| − | + | TPessoaNova = class | |
| − | TPessoaNova = class | + | public |
| − | public | + | property Nome: string read FNome write FNome; |
| − | + | property CNPJ: TCNPJ read FCNPJ write FCNPJ; | |
| − | + | end; | |
| − | end; | + | |
| − | + | var | |
| − | var | + | Pessoa: TPessoaNova; |
| − | + | begin | |
| − | begin | + | Pessoa.Nome := 'Tecsystem'; |
| − | + | Pessoa.CNPJ := '99.999.999/0001-91'; | |
| − | + | if Pessoa.CNPJ.EhValido() then | |
| − | + | // ... | |
| − | + | end; | |
| − | end; | + | |
Isso pode ser possível através do uso de Tiny Types. O padrão indica o uso de uma classe para resolver o problema, mas o Delphi oferece um tipo de dados mais interessante nesse caso que é o record type. A interface do record seria basicamente isso: | Isso pode ser possível através do uso de Tiny Types. O padrão indica o uso de uma classe para resolver o problema, mas o Delphi oferece um tipo de dados mais interessante nesse caso que é o record type. A interface do record seria basicamente isso: | ||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
TCNPJ = record | TCNPJ = record | ||
private | private | ||
| − | + | FValor: string; | |
| − | + | ||
| − | + | ||
public | public | ||
class operator Implicit(const CNPJ: string): TCNPJ; | class operator Implicit(const CNPJ: string): TCNPJ; | ||
class operator Implicit(const CNPJ: TCNPJ): string; | class operator Implicit(const CNPJ: TCNPJ): string; | ||
| + | function EhValido(): Boolean; | ||
| + | // ... | ||
| + | end; | ||
| − | + | A implementação é normal, com exceção do operator overloading class operator implicit que resolve um erro em tempo de compilação ao tentar atribuir um valor a uma property do tipo record. | |
| − | + | Segue abaixo a unit de exemplo com o código completo das funcionalidades para consulta: | |
| − | + | ||
| + | unit DocsBR; | ||
| + | |||
| + | interface | ||
| + | |||
| + | type | ||
| + | |||
| + | TCNPJ = record | ||
| + | private | ||
| + | FUnformatted: string; | ||
| + | function GetFormatted: string; | ||
| + | |||
| + | public | ||
| + | class operator Implicit(const CNPJ: string): TCNPJ; | ||
| + | class operator Implicit(const CNPJ: TCNPJ): string; | ||
| + | |||
| + | function IsValid(): Boolean; | ||
| + | |||
| + | property Unformatted: string read FUnformatted; | ||
| + | property Formatted: string read GetFormatted; | ||
| + | |||
| + | end; | ||
| + | |||
| + | TCPF = record | ||
| + | private | ||
| + | FUnformatted: string; | ||
| + | |||
| + | function GetFormatted: string; | ||
| + | |||
| + | public | ||
| + | class operator Implicit(const CPF: string): TCPF; | ||
| + | class operator Implicit(const CPF: TCPF): string; | ||
| + | |||
| + | function IsValid(): Boolean; | ||
| + | |||
| + | property Unformatted: string read FUnformatted; | ||
| + | property Formatted: string read GetFormatted; | ||
| + | |||
| + | end; | ||
| + | |||
| + | implementation | ||
| + | |||
| + | uses | ||
| + | MaskUtils, StrUtils, SysUtils; | ||
| + | |||
| + | function Clear(const Doc: string): string; | ||
| + | var | ||
| + | Letter: Char; | ||
| + | begin | ||
| + | |||
| + | Result := ''; | ||
| + | |||
| + | for Letter in Doc do | ||
| + | begin | ||
| + | |||
| + | if Letter in ['0'..'9'] then | ||
| + | Result := Result + Letter; | ||
| + | |||
| + | end; | ||
| + | |||
end; | end; | ||
| − | |||
| − | |||
| − | |||
| − | |||
| − | + | { TCNPJ } | |
| − | + | ||
| − | + | class operator TCNPJ.Implicit(const CNPJ: string): TCNPJ; | |
| − | + | begin | |
| − | + | Result.FUnformatted := Clear(CNPJ); | |
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
end; | end; | ||
| − | + | ||
| − | + | function TCNPJ.GetFormatted: string; | |
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | function | + | |
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
begin | begin | ||
| − | + | Result := MaskUtils.FormatMaskText('00.000.000/0000-00;0; ', FUnformatted); | |
| − | + | ||
| − | + | ||
| − | + | ||
end; | end; | ||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | + | class operator TCNPJ.Implicit(const CNPJ: TCNPJ): string; | |
| − | + | ||
| − | + | ||
begin | begin | ||
| − | Result := | + | Result := CNPJ.FUnformatted; |
| − | end | + | end; |
| − | + | ||
| + | function TCNPJ.IsValid: Boolean; | ||
| + | const | ||
| + | BLACKLIST: array[0..9] of string = ('00000000000000', | ||
| + | '11111111111111', | ||
| + | '22222222222222', | ||
| + | '33333333333333', | ||
| + | '44444444444444', | ||
| + | '55555555555555', | ||
| + | '66666666666666', | ||
| + | '77777777777777', | ||
| + | '88888888888888', | ||
| + | '99999999999999'); | ||
| + | var | ||
| + | IsInvalidSize: Boolean; | ||
| + | IsBlacklisted: Boolean; | ||
| + | I: Integer; | ||
| + | D1, D2: Integer; | ||
begin | begin | ||
| − | + | ||
| − | + | IsInvalidSize := Length(FUnformatted) <> 14; | |
| − | + | IsBlacklisted := AnsiIndexStr(FUnformatted, BLACKLIST) >= 0; | |
| − | + | ||
| + | if IsInvalidSize or IsBlacklisted then | ||
begin | begin | ||
| − | + | Result := False; | |
| − | + | end | |
| − | + | else | |
| − | + | begin | |
| − | + | ||
| − | + | ||
| − | end | + | |
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
D1 := 0; | D1 := 0; | ||
| − | + | ||
| − | + | for I := 1 to 12 do | |
| − | + | begin | |
| − | + | ||
| − | + | if I < 5 then | |
| − | + | D1 := D1 + (StrToInt(FUnformatted[I]) * (6 - I)) | |
| − | + | else | |
| − | + | D1 := D1 + (StrToInt(FUnformatted[I]) * (14 - I)); | |
| − | + | ||
| − | + | end; | |
| + | |||
| + | D1 := 11 - (D1 mod 11); | ||
| + | |||
| + | if D1 >= 10 then | ||
| + | D1 := 0; | ||
| + | |||
| + | D2:= D1 * 2; | ||
| + | |||
| + | for I := 1 to 12 do | ||
| + | begin | ||
| + | |||
| + | if I < 6 then | ||
| + | D2 := D2 + (StrToInt(FUnformatted[I]) * (7 - I)) | ||
| + | else | ||
| + | D2 := D2 + (StrToInt(FUnformatted[I]) * (15 - I)); | ||
| + | |||
| + | end; | ||
| + | |||
| + | D2 := 11 - (D2 mod 11); | ||
| + | |||
| + | if D2 >= 10 then D2 :=0; | ||
| + | |||
| + | Result := (IntToStr(D1) + IntToStr(D2)) = Copy(FUnformatted, 13, 2); | ||
| + | |||
end; | end; | ||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
end; | end; | ||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | + | { TCPF } | |
| − | + | class operator TCPF.Implicit(const CPF: string): TCPF; | |
| + | begin | ||
| + | Result.FUnformatted := Clear(CPF); | ||
| + | end; | ||
| − | + | function TCPF.GetFormatted: string; | |
begin | begin | ||
| − | Result := | + | Result := MaskUtils.FormatMaskText('000.000.000-00;0; ', FUnformatted); |
| − | end | + | end; |
| − | + | ||
| + | class operator TCPF.Implicit(const CPF: TCPF): string; | ||
begin | begin | ||
| − | + | Result := CPF.FUnformatted; | |
| − | D1 := 0; | + | end; |
| − | + | ||
| − | for I := 1 to 9 do | + | function TCPF.IsValid: Boolean; |
| − | + | const | |
| + | BLACKLIST: array[0..9] of string = ('00000000000', | ||
| + | '11111111111', | ||
| + | '22222222222', | ||
| + | '33333333333', | ||
| + | '44444444444', | ||
| + | '55555555555', | ||
| + | '66666666666', | ||
| + | '77777777777', | ||
| + | '88888888888', | ||
| + | '99999999999'); | ||
| + | var | ||
| + | IsInvalidSize: Boolean; | ||
| + | IsBlacklisted: Boolean; | ||
| + | I: Integer; | ||
| + | D1, D2: Integer; | ||
| + | begin | ||
| + | |||
| + | IsInvalidSize := Length(FUnformatted) <> 11; | ||
| + | IsBlacklisted := AnsiIndexStr(FUnformatted, BLACKLIST) >= 0; | ||
| + | |||
| + | if IsInvalidSize or IsBlacklisted then | ||
| + | begin | ||
| + | Result := False; | ||
| + | end | ||
| + | else | ||
| + | begin | ||
| + | |||
| + | D1 := 0; | ||
| + | |||
| + | for I := 1 to 9 do | ||
| + | D1 := D1 + (StrToInt(FUnformatted[I]) * (11 - I)); | ||
| − | + | D1 := 11 - (D1 mod 11); | |
| − | + | if D1 >= 10 then | |
| − | + | ||
| − | + | D1 := 0; | |
| − | + | D2 := D1 * 2; | |
| − | + | for I := 1 to 9 do | |
| − | + | D2 := D2 + (StrToInt(FUnformatted[I]) * (12 - I)); | |
| − | + | ||
| − | + | D2 := 11 - (D2 mod 11); | |
| − | + | if D2 >= 10 then | |
| − | + | D2 :=0; | |
| − | + | ||
| − | + | Result := (IntToStr(D1) + IntToStr(D2)) = Copy(FUnformatted, 10, 2); | |
| − | + | ||
| + | end; | ||
| + | |||
end; | end; | ||
| − | + | ||
| − | + | end. | |
| − | + | ||
| − | end. | + | |