Классы и методы Delphi. Нужна реализация одного метода для нескольких классов

#class #oop #delphi

#класс #ооп #delphi

Вопрос:

Итак, например, у меня есть классы овощей для фермы.

 TVegetable = class

TCarrot = class(TVegetable)
TTomato = class(TVegetable)
  

Мне нужны два разных класса каждого овоща, один для супермаркетов, а другой для фабрик.

 TCarrotSupermarket = class(TCarrot)
TCarrotFactory = class(TCarrot)
  

Эти классы идентичны, за исключением кода для одного метода:

 procedure Utilization;
  

TCarrotSupermarket.Utilization работает с супермаркетами, TCarrotFactory.Utilization работает с фабриками.

Один идентичный код для Utilization мне нужен для TCarrotSupermarket.Utilization , TTomatoSupermarket.Utilization TPotatoSupermarket.Utilization TCarrotFactory.Utilization , и другой код для TTomatoFactory.Utilization , TPotatoFactory.Utilization ,,,.

Каков наилучший способ написать код для Utilization только дважды (для супермаркетов и фабрик) и использовать его в соответствующих классах?

Комментарии:

1. Возможно, информации обо всей проблеме недостаточно, но, похоже, вы ищете что-то вроде шаблона Visitor: en.wikipedia.org/wiki/Visitor_pattern

2. Я думаю, вы выбрали неправильный подход к проектированию. Морковка всегда должна быть просто морковкой, а помидор всегда должен быть просто помидором, независимо ОТ ТОГО, КАК они используются. Я думаю, вам нужно отделить логику вашего супермаркета / фабрики от самих классов Vegetable и создать для них отдельный набор классов VegetableUser, тогда вы можете либо 1) передать объект Vegetable объекту User, который будет его использовать, и / или 2) передать пользовательский объект объекту Vegetable, когда Пользователь его использует.

3. Спасибо, я хотел бы разделить. В реальном мире овощи — это классы, отличные от TThread в устаревшем приложении Win32. Мне нужно использовать эти потоки в службе Windows. И использование синхронизируется.

4. Если это так, возможно, ваша модель реального мира тоже неверна. Если бы я запрограммировал морковку, это был бы не TThread, а простой класс или даже запись, содержащий свойства и методы, которые применяются к самой морковке. Будучи оранжевым, растущим, с количеством бета-каротина, это морковные вещи. Продаваемый или обрабатываемый — нет. Итак, у меня были бы морковь, помидор, фабрика, супермаркет и, возможно, при необходимости, отдельные CarrotProcessor и TomatoProcessor для фабрики, которые в идеале используют один интерфейс IVegetableProcessor, поэтому самой фабрике не обязательно знать о конкретных овощах.

5. Кстати, я не думаю, что какой-либо из этих объектов является потоком. Однако у них может быть поток в качестве помощника для выполнения их работы.

Ответ №1:

Добро пожаловать в Pattern Design. Ваш случай — это шаблон стратегии

 class TStrategyVegetable = class(TVegetable)
  FUtil: TUtilization
public
  procedure Create(util: TUtilization);
  procedure Utilization();
end

procedure TStrategyVegetable.Create(util: TUtilization)
begin
  FUtil := util
end
procedure TStrategyVegetable.Utilization;
begin
  FUtil.Utilization;
end
  

Затем в коде:

 carrotSupermarket = TCarrotSupermarket.Create(TCarrotSupermarketUtil.Create);
carrotFactory = TCarrotFactory.Create(TCarrotFactoryUtil.Create);
  

Ответ №2:

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

 IFactoryInterface=interface(IUnknown) ['{someGUID}']
  procedure Utilization;
end;

ISuperMarketInterface=interface(IUnknown) ['{AnotherGUID}']
  procedure Utilization;
end;

TVegetable = class (TSomeObject,IFactoryInterface,ISupermarketInterface)
protected
  // the routines tha do the actual implementation
  // can be regular virtual, dynamic, or whatever
  procedure FactoryUtilization; 
  procedure SuperMarketUtilization; 
  // link up the interfaces using method resolution
  procedure IFactoryInterface.Utilization=FactoryUtilization;
  procedure ISupermarketInterface.Utilization=SuperMarketUtilization;

{ in case not derived from TInterfacedObject, 
  You'll have to add _AddRef,_Release and 
  QueryInterface methods too }

end;

// the other implementations can be as before
TCarrot = class(TVegetable)
TTomato = class(TVegetable)
  

Тогда при использовании кода это должно выглядеть примерно так:

 VAR lSOmeVegetable,lAnotherVegetable:TVegetable;
... 
lSomeVegetable:=TCarrot.Create
lanotherVegetable:=Tomato.Create
...
// call using buolt-in type checking
(lSOmeVegetable as IFactoryInterface).Utilization;
(lAnotherVegetable as ISuperMarketInterface).Utilization;
  

Или иным образом с использованием подпрограммы supports

 VAR lFactory:IFactoryInterface;
...
if supports(lSomeVegetable,IFactoryInterface,lFactory) then
  lFactory.Utilization
...
  

Надеюсь, это вам немного поможет. Вот указатель на некоторую соответствующую документацию

Комментарии:

1. Спасибо, но я не могу реализовать как FactoryUtilization, так и SuperMarketUtilization в базовом классе. Это для повторного использования кода в двух разных приложениях: супермаркет и фабрика

2. В этом случае шаблон stategy (предложенный snake), вероятно, является лучшим способом, поскольку он позволяет создавать ваше использование в разных классах / единицах. Другой альтернативой может быть использование 2 вспомогательных классов: TvegetableSupermarketHelper и TVegetableFactoryHelper=class helper для TVegetable. Имейте в виду, что существуют некоторые ограничения на использование вспомогательных классов. Смотрите docwiki.embarcadero.com/RADStudio/Rio/en /…