Quels sont les bons usages pour les aides de classe?
Question
Delphi (et probablement beaucoup d’autres langages) a des aides de classe. Celles-ci permettent d'ajouter des méthodes supplémentaires à une classe existante. Sans faire de sous-classe.
Alors, quelles sont les bonnes utilisations pour les aides de classe?
La solution
je les utilise:
- À insérez des énumérateurs dans les classes VCL qui ne les implémentent pas.
- Pour améliorer les classes VCL.
-
Pour ajouter des méthodes à la classe TStrings afin que je puisse utiliser les mêmes méthodes dans mes listes dérivées et dans 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 }
-
Pour simplifier l'accès aux champs d'enregistrement et supprimer la conversion .
Autres conseils
Par exemple, si vous souhaitez une fonctionnalité supplémentaire pour une classe d'instance existante et pour une raison quelconque, vous ne pouvez pas modifier la source existante. Vous pouvez créer un assistant de classe pour ajouter cette fonctionnalité.
Exemple:
type
TStringsHelper = class helper for TStrings
public
function IsEmpty: Boolean;
end;
function TStringsHelper.IsEmpty: Boolean;
begin
Result := Count = 0;
end;
À chaque fois, nous utilisons maintenant une instance de (une sous-classe de) TStrings, et TStringsHelper est dans la portée. Nous avons accès à la méthode IsEmpty.
Exemple:
procedure TForm1.Button1Click(Sender: TObject);
begin
if Memo1.Lines.IsEmpty then
Button1.Caption := 'Empty'
else
Button1.Caption := 'Filled';
end;
Remarques:
- Les aides de classe peuvent être stockées dans une unité séparée, vous pouvez donc ajouter vos propres aides de classe astucieuses. Assurez-vous de donner à ces unités un nom facile à retenir, tel que ClassesHelpers pour les aides de l'unité Classes.
- Il existe également des assistants d'enregistrement.
- S'il existe plusieurs aides de classe dans la portée, attendez-vous à quelques problèmes, vous ne pouvez utiliser qu'un seul assistant.
Cela ressemble beaucoup aux méthodes d'extension en C # 3 (et VB9). La meilleure utilisation que je connaisse pour eux est l’extension de IEnumerable<T>
(et IQueryable<T>
) qui permet à LINQ de travailler contre des séquences arbitraires:
var query = someOriginalSequence.Where(person => person.Age > 18)
.OrderBy(person => person.Name)
.Select(person => person.Job);
(ou autre chose, bien sûr). Tout cela est faisable car les méthodes d'extension vous permettent d'enchaîner efficacement des appels à des méthodes statiques qui prennent le même type à leur retour.
Ils sont très utiles pour les plug-ins. Par exemple, supposons que votre projet définisse une certaine structure de données et que celle-ci soit enregistrée sur le disque d’une certaine manière. Mais alors, un autre programme fait quelque chose de très similaire, mais le fichier de données est différent. Mais vous ne voulez pas surcharger votre fichier EXE avec un paquet de code d'importation pour une fonctionnalité que beaucoup de vos utilisateurs n'auront pas besoin d'utiliser. Vous pouvez utiliser un framework de plug-in et placer les importateurs dans un plug-in qui fonctionnerait comme suit:
type
TCompetitionToMyClass = class helper for TMyClass
public
constructor Convert(base: TCompetition);
end;
Et ensuite définissez le convertisseur. Une mise en garde: un assistant de classe n'est pas un ami de classe . Cette technique ne fonctionnera que s'il est possible de configurer complètement un nouvel objet TMyClass via ses méthodes et propriétés publiques. Mais si vous le pouvez, cela fonctionne vraiment bien.
La première fois que je me souviens avoir vécu ce que vous appelez & "; des aides de classe &"; était tout en apprenant Objective C. Cocoa (framework Objective C d’Apple) utilise ce que l’on appelle & "Catégories. &";
Une catégorie vous permet d'étendre une classe existante en ajoutant vos propres méthodes sans sous-classe. En fait, Cocoa vous encourage à éviter les sous-classes lorsque cela est possible. Il est souvent logique de sous-classer, mais il est souvent possible de l’éviter en utilisant des catégories.
Un bon exemple d'utilisation d'une catégorie dans Cocoa est ce qu'on appelle & "Code de valeur de clé (KVC) &"; et " observation de la valeur de la clé (KVO). "
Ce système est mis en œuvre en utilisant deux catégories (NSKeyValueCoding et NSKeyValueObserving). Ces catégories définissent et implémentent des méthodes pouvant être ajoutées à la classe de votre choix. Par exemple, Cocoa ajoute & "; Conformité &"; KVC / KVO en utilisant ces catégories pour ajouter des méthodes à NSArray telles que:
- (id)valueForKey:(NSString *)key
La classe NSArray n’a ni déclaration ni implémentation de cette méthode. Cependant, à travers l'utilisation de la catégorie. Vous pouvez appeler cette méthode sur n’importe quelle classe NSArray. Vous n'êtes pas obligé de sous-classer NSArray pour obtenir 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’utilisation de cette technique facilite l’ajout du support KVC / KVO à vos propres classes. Les interfaces Java vous permettent d’ajouter des déclarations de méthode, mais les catégories vous permettent également d’ajouter les implémentations réelles aux classes existantes.
Comme le montre GameCat, TStrings est un bon candidat pour éviter la saisie:
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;
Avez-vous déjà eu besoin d'accéder à des chaînes multilignes dans le registre?
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;
Je ne recommanderais pas de les utiliser, car j'ai lu ce commentaire:
& "Le plus gros problème avec la classe aides, de la p.o.v de les utiliser dans vos propres applications, le fait que seul un assistant de classe pour une donnée La classe peut être dans la portée à tout moment. " ... & "C'est-à-dire si vous avez deux aides en portée, un seul sera reconnu par le compilateur. Vous n'en aurez pas des avertissements ou même des allusions sur un autre les aides qui peuvent être cachés. & ";
http://davidglassborow.blogspot.com /2006/05/class-helpers-good-or-bad.html
Je les ai utilisées pour rendre les méthodes de classe disponibles cohérentes d'une classe à l'autre: Ajouter Open / Close et Show / Hide à toutes les classes d'un " type " plutôt que seulement les propriétés actives et visibles.