Как хорошо использовать помощников в классе? - PullRequest
34 голосов
/ 31 октября 2008

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

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

Ответы [ 8 ]

33 голосов
/ 31 октября 2008

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

13 голосов
/ 31 октября 2008

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

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

Пример:

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.
  • Есть также помощники по записи.
  • Если в области видимости есть несколько помощников классов, ожидайте проблем, можно использовать только один помощник.
6 голосов
/ 31 октября 2008

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

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

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

4 голосов
/ 31 октября 2008

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

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

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

3 голосов
/ 31 октября 2008

Как показывает 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;
3 голосов
/ 31 октября 2008

Впервые я помню, как испытывал то, что вы называете «помощниками классов», во время изучения Objective C. Какао (платформа Apple Objective C) использует так называемые «Категории».

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

Хорошим примером использования категории в Какао является то, что называется «Код значения ключа (KVC)» и «Наблюдение значения ключа (KVO)».

Эта система реализована с использованием двух категорий (NSKeyValueCoding и NSKeyValueObserving). Эти категории определяют и реализуют методы, которые могут быть добавлены в любой класс, который вы хотите. Например, Какао добавляет «соответствие» в 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 голосов
/ 04 июля 2009

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

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

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

0 голосов
/ 27 января 2009

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...