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?

Était-ce utile?

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

Au début, j'étais plutôt sceptique à propos des aides de classe. Mais ensuite, j'ai lu une entrée de blog intéressante et maintenant je suis convaincu qu’ils sont vraiment utiles.

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.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top