#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 /…