Как хорошо использовать помощников в классе?

30

В Delphi (и, вероятно, много других языков) есть помощники класса. Они обеспечивают способ добавления дополнительных методов к существующему классу. Без создания подкласса.

Итак, что полезно для помощников классов?

Теги:
class
helper

8 ответов

31
Лучший ответ

Я использую их:

  • Чтобы вставить перечисления в классы VCL, которые их не реализуют.
  • В улучшить классы VCL.
  • Чтобы добавить методы в класс TStrings, я могу использовать те же методы в мои производные списки и в 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 }
    
  • Чтобы упростить доступ к полям записи и удалить кастинг.

11
Сначала я был скептически относился к помощникам класса. Но затем я прочитал интересную запись в блоге, и теперь я убежден, что они действительно полезны.

Например, если вам нужна дополнительная функциональность для существующего класса экземпляра, и по какой-то причине вы не можете изменить существующий источник. Вы можете создать помощник класса, чтобы добавить эту функциональность.

Пример:

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

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

Каждый раз мы теперь используем экземпляр (подкласс) TStrings и TStringsHelper находится в пределах области действия. У нас есть доступ к методу IsEmpty.

Пример:

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

Примечания:

  • Помощники класса могут храниться в отдельном блоке, поэтому вы можете добавить своих собственных помощников класса. Не забудьте дать этим устройствам легко запоминающееся имя, например ClassesHelpers, для помощников для модуля Classes.
  • Есть также помощники записи.
  • Если в области есть несколько помощников классов, ожидайте некоторые проблемы, можно использовать только один помощник.
  • 3
    Читали ли вы комментарий: «Самая большая проблема с помощниками классов, из-за необходимости их использования в ваших собственных приложениях, заключается в том, что только ОДИН помощник класса для данного класса может находиться в области видимости в любое время». ... "То есть, если у вас есть два помощника в области видимости, компилятор распознает только ОДИН. Вы не получите никаких предупреждений или даже намеков о любых других помощниках, которые могут быть скрыты".
  • 0
    Помощники CLASS могут наследовать от других помощников CLASS, так что вы можете - фактически - иметь несколько активных помощников CLASS одновременно (но они должны знать друг о друге, и, таким образом, вы можете иметь только непрерывную «строку» помощников CLASS активными). То есть, если у вас есть Helper1, а Helper2 наследуется от Helper1, а Helper3 наследуется от Helper2, то вы не можете иметь только активный Helper3 (без каких-либо директив условной компиляции), но должны иметь все три помощника в области действия - вы можете иметь только Helper1, только Helper1 + Helper2 или все три активных одновременно - только без Helper1 + Helper3).
6

Это очень похоже на методы расширения в С# 3 (и VB9). Лучшее использование, которое я видел для них, - это расширения для IEnumerable<T>IQueryable<T>), которые позволяют LINQ работать с произвольными последовательностями:

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

(или что-то еще, конечно). Все это выполнимо, поскольку методы расширения позволяют эффективно объединять вызовы статическим методам, которые принимают тот же тип, что и они.

  • 5
    В качестве примечания, у Delphi есть помощники в классе с 2000 года, и у нас есть патент на это понятие. Ник Ходжес, менеджер по продуктам Delphi Embarcadero Technologies
  • 0
    @Nick: О какой версии Delphi вы говорите, у которой были помощники в классе с 2000 года? Я помню только то, что читал о них, когда они использовались в Delphi 2007 для создания непрерывного релиза. Это было через 6 лет после того года, когда вы утверждаете, что они у Delphi.
Показать ещё 2 комментария
4

Они очень полезны для плагинов. Например, пусть ваш проект определяет определенную структуру данных и сохраняет ее на диске определенным образом. Но тогда какая-то другая программа делает что-то очень похожее, но файл данных отличается. Но вы не хотите раздувать ваш EXE с кучей кода импорта для функции, которую многие ваши пользователи не будут использовать. Вы можете использовать платформу плагина и поместить импортеров в плагин, который будет работать следующим образом:

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

И затем определите конвертер. Одно предостережение: помощник класса не является другом класса. Этот метод будет работать только в том случае, если можно полностью настроить новый объект TMyClass с помощью своих общедоступных методов и свойств. Но если вы можете, он работает очень хорошо.

3

Как показывает GameCat, TStrings - хороший кандидат, чтобы избежать ввода текста:

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;

Вам когда-нибудь приходилось обращаться к многострочным строкам в реестре?

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;
  • 4
    Вау, переопределение «по умолчанию»! Невероятный. И, возможно, отличный источник странных ошибок :)
  • 0
    Согласен, это немного не так. Но я использую помощников класса в основном в ограниченном объеме, так что это не было проблемой - до сих пор ...
3

В первый раз, когда я помню, что вы испытывали то, что вы называете "помощниками класса", было при изучении Objective C. Cocoa (инфраструктура Apple Objective C) использует так называемые "категории".

Категория позволяет расширить существующий класс, добавив собственные методы без подкласса. Фактически Cocoa рекомендует вам избегать подклассов, когда это возможно. Часто это имеет смысл для подкласса, но часто его можно избежать с помощью категорий.

Хорошим примером использования категории в Cocoa является то, что называется "Key Value Code (KVC)" и "Key Value Observing (KVO)".

Эта система реализована с использованием двух категорий (NSKeyValueCoding и NSKeyValueObserving). Эти категории определяют и реализуют методы, которые могут быть добавлены в любой класс, который вы хотите. Например, Cocoa добавляет "соответствие" KVC/KVO с помощью этих категорий для добавления методов в NSArray, таких как:

- (id)valueForKey:(NSString *)key

Класс NSArray не имеет ни декларации, ни реализации этого метода. Однако, используя категорию. Вы можете вызвать этот метод для любого класса NSArray. Вам не требуется подкласс NSArray для получения соответствия KVC/KVO.

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

Используя этот метод, вы легко можете добавить поддержку KVC/KVO в свои собственные классы. Интерфейсы Java позволяют добавлять объявления методов, но категории позволяют также добавлять фактические реализации в существующие классы.

2

Я бы не рекомендовал их использовать, так как я прочитал этот комментарий:

"Самая большая проблема с классом помощники, из p.o.v их использования в ваших собственных приложениях, это факт что только один помощник класса для данного класс может быть в любой момент".... "То есть, если у вас есть два помощника в рамках, будет признан только ОДИН компилятором. Вы не получите никакой предупреждения или даже намеки на любые другие которые могут быть скрыты.

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

  • 3
    Я прочитал этот комментарий. Но это не причина не использовать их. Просто используйте их осторожно, как любой другой инструмент.
  • 1
    @GamecatisToonKrij даже не намекает, это означает, что разработчики теперь должны сканировать все источники сторонних библиотек (не только один раз, но и для каждого обновления тоже), чтобы обнаружить «помощников по конфликтам» ...
0

Я видел, как они использовались для создания доступных методов класса, согласованных между классами: Добавление Open/Close и Show/Hide ко всем классам данного типа, а не только к активным и видимым свойствам.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню