Jakie są dobre zastosowania dla pomocników klasowych?

Delphi (i prawdopodobnie wiele innych języków) posiada helpery klas. Zapewniają one możliwość dodania dodatkowych metod do istniejącej klasy. Bez tworzenia podklasy.

Więc, jakie są dobre zastosowania dla pomocników klasowych?

Author: menjaraz, 2008-10-31

8 answers

Używam ich:

  • do Wstaw enumeratory do klas VCL, które ich nie implementują.
  • do wzmocnij klasy VCL.
  • Aby dodać metody do klasy TStrings, abym mógł używać tych samych metod w moich listach pochodnych i w 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 }
    
  • Aby uprościć dostęp do pól rekordów i usunąć casting .

 34
Author: gabr,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-31 13:44:29

Na początku byłem trochę sceptyczny w stosunku do pomocników klasowych. Ale potem przeczytałem ciekawy wpis na blogu i teraz jestem przekonany, że są rzeczywiście przydatne.

Na przykład, jeśli potrzebujesz dodatkowej funkcjonalności dla istniejącej klasy instancji i z jakiegoś powodu nie możesz zmienić istniejącego źródła. Możesz utworzyć pomocnika klasy, aby dodać tę funkcjonalność.

Przykład:

type
  TStringsHelper = class helper for TStrings
  public
    function IsEmpty: Boolean;
  end;

function TStringsHelper.IsEmpty: Boolean;
begin
  Result := Count = 0;
end;

Za każdym razem używamy instancji (podklasy) TStrings i TStringsHelper jest w zasięgu. Mamy dostęp do metody IsEmpty.

Przykład:

procedure TForm1.Button1Click(Sender: TObject);
begin
  if Memo1.Lines.IsEmpty then
    Button1.Caption := 'Empty'
  else
    Button1.Caption := 'Filled';
end;

Uwagi:

  • pomocniki klas mogą być przechowywane w osobnej jednostce, więc możesz dodać własne sprytne pomocniki klas. Pamiętaj, aby nadać tym jednostkom łatwą do zapamiętania nazwę, taką jak ClassesHelpers dla pomocników dla jednostki Classes.
  • istnieją również pomocnicy rekordów.
  • jeśli w zasięgu jest wiele pomocników klas, spodziewaj się pewnych problemów, tylko jednego można użyć pomocnika.
 14
Author: Toon Krijthe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-12-05 13:51:33

To brzmi bardzo podobnie do metod rozszerzeń w C # 3 (i VB9). Najlepsze zastosowanie jakie widziałem dla nich to rozszerzenia IEnumerable<T> (i IQueryable<T>), które pozwalają LINQ pracować z dowolnymi sekwencjami:

var query = someOriginalSequence.Where(person => person.Age > 18)
                                .OrderBy(person => person.Name)
                                .Select(person => person.Job);

(lub cokolwiek, oczywiście). Wszystko to jest wykonalne, ponieważ metody rozszerzeń pozwalają efektywnie łączyć wywołania do metod statycznych, które przyjmują ten sam typ, co zwracane.

 6
Author: Jon Skeet,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-31 13:18:30

Są bardzo przydatne dla wtyczek. Załóżmy na przykład, że twój projekt definiuje pewną strukturę danych i jest zapisywany na dysku w określony sposób. Ale inny program robi coś bardzo podobnego, ale plik danych jest inny. Ale nie chcesz nadmuchać EXE z kilkoma kodami importu dla funkcji, której wielu użytkowników nie będzie musiało używać. Możesz użyć frameworka wtyczki i umieścić go w wtyczce, która działałaby tak:

type
   TCompetitionToMyClass = class helper for TMyClass
   public
      constructor Convert(base: TCompetition);
   end;

A następnie zdefiniuj konwerter. Jedno zastrzeżenie: klasa helper nie jest klasą friend. Ta technika zadziała tylko wtedy, gdy możliwe jest całkowite skonfigurowanie nowego obiektu TMyClass za pomocą jego publicznych metod i właściwości. Ale jeśli możesz, to działa naprawdę dobrze.

 4
Author: Mason Wheeler,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-31 20:37:49

Pierwszy raz pamiętam, że doświadczyłem tego, co nazywacie "pomocnikami klas" , podczas nauki Objective C. Cocoa (Framework Objective C firmy Apple) używa tak zwanych "kategorii"."

Kategoria pozwala na rozszerzenie istniejącej klasy poprzez dodanie własnych metod bez podklasowania. W rzeczywistości Cocoa zachęca do unikania podklasowania, gdy to możliwe. Często podklasa ma sens, ale często można jej uniknąć za pomocą kategorii.

Dobry przykład użycia kategorii w Cocoa to tzw. " Key Value Code (KVC)" i " Key Value Observing (KVO)."

Ten system jest zaimplementowany przy użyciu dwóch kategorii (NSKeyValueCoding i NSKeyValueObserving). Te kategorie definiują i implementują metody, które można dodać do dowolnej klasy. Na przykład Cocoa dodaje "zgodność" do KVC/KVO, używając tych kategorii, aby dodać metody do NSArray, takie jak:

- (id)valueForKey:(NSString *)key

Klasa NSArray nie posiada ani deklaracji, ani implementacji tej metody. Jednakże, poprzez użycie kategorii. Możesz wywołać tę metodę na dowolnej klasie NSArray. Nie musisz podklasować NSArray, aby uzyskać zgodność z KVC/KVO.

NSArray *myArray = [NSArray array]; // Make a new empty array
id myValue = [myArray valueForKey:@"name"]; // Call a method defined in the category

Użycie tej techniki ułatwia dodanie obsługi KVC/KVO do własnych klas. Interfejsy Java pozwalają dodawać deklaracje metod, ale kategorie pozwalają również dodawać rzeczywiste implementacje do istniejących klas.

 3
Author: Robert Walker,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-31 14:10:26

Jak pokazuje GameCat, TStrings jest dobrym kandydatem, aby uniknąć typowania:

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;

Czy kiedykolwiek potrzebowałeś dostępu do ciągów wielowierszowych w rejestrze?

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;
 3
Author: Uwe Raabe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-31 14:27:36

Nie polecam ich używać, ponieważ przeczytałem ten komentarz:

" największy problem z klasą pomocnicy, od P. O. v z ich używania w swoich własnych aplikacjach, jest fakt że tylko jeden Pomocnik klasy dla danego klasa może być w zasięgu w każdej chwili." ... "To znaczy, jeśli masz dwóch pomocników w zakresie, tylko jeden zostanie uznany przez kompilator. Nie dostaniesz żadnych ostrzeżenia lub nawet podpowiedzi o innych pomocnicy, którzy mogą być Ukryty."

Http://davidglassborow.blogspot.com/2006/05/class-helpers-good-or-bad.html

 2
Author: mjn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-07-04 05:57:25

Widziałem je używane do udostępniania dostępnych metod klas spójnych między klasami: dodawanie Open / Close I Show / Hide do wszystkich klas danego "typu", a nie tylko aktywnych i widocznych właściwości.

 0
Author: Jamo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-01-27 11:32:59