Quali sono i buoni usi per gli aiutanti di classe?
Domanda
Delphi (e probabilmente molti altri linguaggi) ha degli aiutanti di classe.Questi forniscono un modo per aggiungere metodi extra a una classe esistente.Senza creare una sottoclasse.
Quindi, quali sono i buoni usi per gli aiutanti di classe?
Soluzione
Li sto usando:
- A inserire enumeratori nelle classi VCL che non li implementano.
- A migliorare Classi VCL.
Per aggiungere metodi alla classe TStrings in modo da poter utilizzare gli stessi metodi in le mie liste derivate e in TStringList.
TGpStringListHelper = class helper for TStringList public function Last: string; function Contains(const s: string): boolean; function FetchObject(const s: string): TObject; procedure Sort; procedure Remove(const s: string); end; { TGpStringListHelper }
Per semplificare l'accesso ai campi record e rimuovere la fusione.
Altri suggerimenti
All'inizio ero un po 'scettico riguardo agli aiutanti di classe. Ma poi ho letto un interessante post di blog e ora sono convinto che siano davvero utili.
Ad esempio, se si desidera funzionalità extra per una classe di istanza esistente e per qualche motivo non è possibile modificare l'origine esistente. È possibile creare un helper di classe per aggiungere questa funzionalità.
Esempio:
type
TStringsHelper = class helper for TStrings
public
function IsEmpty: Boolean;
end;
function TStringsHelper.IsEmpty: Boolean;
begin
Result := Count = 0;
end;
Ogni volta, ora utilizziamo un'istanza di (una sottoclasse di) TStrings e TStringsHelper rientra nell'ambito. Abbiamo accesso al metodo IsEmpty.
Esempio:
procedure TForm1.Button1Click(Sender: TObject);
begin
if Memo1.Lines.IsEmpty then
Button1.Caption := 'Empty'
else
Button1.Caption := 'Filled';
end;
Note:
- Gli helper di classe possono essere memorizzati in un'unità separata, in modo da poter aggiungere i propri helper di classe eleganti. Assicurati di dare a queste unità un nome facile da ricordare come ClassesHelpers per gli helper dell'unità Classes.
- Ci sono anche helper di registrazione.
- Se ci sono helper di classi multiple nell'ambito, si aspettano alcuni problemi, è possibile utilizzare un solo helper.
Sembra molto simile ai metodi di estensione in C # 3 (e VB9). L'uso migliore che ho visto per loro è l'estensione a IEnumerable<T>
(e IQueryable<T>
) che consente a LINQ di lavorare contro sequenze arbitrarie:
var query = someOriginalSequence.Where(person => person.Age > 18)
.OrderBy(person => person.Name)
.Select(person => person.Job);
(o qualunque altra cosa, ovviamente). Tutto ciò è fattibile perché i metodi di estensione ti consentono di concatenare efficacemente le chiamate a metodi statici che assumono lo stesso tipo del loro ritorno.
Sono molto utili per i plug-in. Ad esempio, supponiamo che il tuo progetto definisca una determinata struttura di dati ed è salvato su disco in un certo modo. Ma poi qualche altro programma fa qualcosa di molto simile, ma il file di dati è diverso. Ma non vuoi gonfiare il tuo EXE con un sacco di codice di importazione per una funzione che molti dei tuoi utenti non dovranno usare. Puoi utilizzare un framework di plug-in e inserire gli importatori in un plug-in che funzioni in questo modo:
type
TCompetitionToMyClass = class helper for TMyClass
public
constructor Convert(base: TCompetition);
end;
E quindi definire il convertitore. Un avvertimento: un aiutante di classe non è un amico di classe. Questa tecnica funzionerà solo se è possibile configurare completamente un nuovo oggetto TMyClass attraverso i suoi metodi e proprietà pubblici. Ma se puoi, funziona davvero bene.
La prima volta che ricordo di aver vissuto ciò che stai chiamando " class helpers " è stato durante l'apprendimento dell'obiettivo C. Il cacao (la struttura dell'obiettivo C di Apple) utilizza quelli che sono chiamati " Categorie. "
Una categoria ti consente di estendere una classe esistente aggiungendo i tuoi metodi senza subclassare. Infatti, il cacao ti incoraggia a evitare la sottoclasse quando possibile. Spesso ha senso effettuare una sottoclasse, ma spesso può essere evitato usando le categorie.
Un buon esempio dell'uso di una categoria in Cocoa è quello che viene chiamato " Key Value Code (KVC) " e " Key Value Observing (KVO). "
Questo sistema è implementato usando due categorie (NSKeyValueCoding e NSKeyValueObserving). Queste categorie definiscono e implementano metodi che possono essere aggiunti a qualsiasi classe desiderata. Ad esempio, Cocoa aggiunge & Quot; conformance & Quot; a KVC / KVO usando queste categorie per aggiungere metodi a NSArray come:
- (id)valueForKey:(NSString *)key
La classe NSArray non ha né una dichiarazione né un'implementazione di questo metodo. Tuttavia, attraverso l'uso della categoria. È possibile chiamare quel metodo su qualsiasi classe NSArray. Non è necessario sottoclassare NSArray per ottenere la conformità KVC / KVO.
NSArray *myArray = [NSArray array]; // Make a new empty array
id myValue = [myArray valueForKey:@"name"]; // Call a method defined in the category
L'uso di questa tecnica semplifica l'aggiunta del supporto KVC / KVO alle tue classi. Le interfacce Java consentono di aggiungere dichiarazioni di metodi, ma le categorie consentono anche di aggiungere le implementazioni effettive alle classi esistenti.
Come mostra GameCat, TStrings è un buon candidato per evitare di scrivere:
type
TMyObject = class
public
procedure DoSomething;
end;
TMyObjectStringsHelper = class helper for TStrings
private
function GetMyObject(const Name: string): TMyObject;
procedure SetMyObject(const Name: string; const Value: TMyObject);
public
property MyObject[const Name: string]: TMyObject read GetMyObject write SetMyObject; default;
end;
function TMyObjectStringsHelper.GetMyObject(const Name: string): TMyObject;
var
idx: Integer;
begin
idx := IndexOf(Name);
if idx < 0 then
result := nil
else
result := Objects[idx] as TMyObject;
end;
procedure TMyObjectStringsHelper.SetMyObject(const Name: string; const Value:
TMyObject);
var
idx: Integer;
begin
idx := IndexOf(Name);
if idx < 0 then
AddObject(Name, Value)
else
Objects[idx] := Value;
end;
var
lst: TStrings;
begin
...
lst['MyName'] := TMyObject.Create;
...
lst['MyName'].DoSomething;
...
end;
Hai mai avuto bisogno di accedere a stringhe multilinea nel registro?
type
TRegistryHelper = class helper for TRegistry
public
function ReadStrings(const ValueName: string): TStringDynArray;
end;
function TRegistryHelper.ReadStrings(const ValueName: string): TStringDynArray;
var
DataType: DWord;
DataSize: DWord;
Buf: PChar;
P: PChar;
Len: Integer;
I: Integer;
begin
result := nil;
if RegQueryValueEx(CurrentKey, PChar(ValueName), nil, @DataType, nil, @DataSize) = ERROR_SUCCESS then begin
if DataType = REG_MULTI_SZ then begin
GetMem(Buf, DataSize + 2);
try
if RegQueryValueEx(CurrentKey, PChar(ValueName), nil, @DataType, PByte(Buf), @DataSize) = ERROR_SUCCESS then begin
for I := 0 to 1 do begin
if Buf[DataSize - 2] <> #0 then begin
Buf[DataSize] := #0;
Inc(DataSize);
end;
end;
Len := 0;
for I := 0 to DataSize - 1 do
if Buf[I] = #0 then
Inc(Len);
Dec(Len);
if Len > 0 then begin
SetLength(result, Len);
P := Buf;
for I := 0 to Len - 1 do begin
result[I] := StrPas(P);
Inc(P, Length(P) + 1);
end;
end;
end;
finally
FreeMem(Buf, DataSize);
end;
end;
end;
end;
Non consiglierei di usarli, poiché ho letto questo commento:
" Il problema più grande con la classe aiutanti, dal p.o.v del loro utilizzo nelle tue applicazioni, è il fatto che solo UN aiutante di classe per un dato la classe può essere nell'ambito in qualsiasi momento. " ... " Cioè, se hai due aiutanti nell'ambito, solo UNO sarà riconosciuto dal compilatore. Non ne otterrai nessuno avvertimenti o anche suggerimenti su qualsiasi altro aiutanti che possono essere nascosti. "
http://davidglassborow.blogspot.com /2006/05/class-helpers-good-or-bad.html
Li ho visti usati per rendere coerenti i metodi di classe disponibili tra le classi: aggiungere Open / Close e Show / Hide a tutte le classi di un dato " type " piuttosto che solo proprietà attive e visibili.