0

So, for example, I have classes of vegetables for a farm.

TVegetable = class

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

I need two different classes of each vegetable, one for supermarkets and another for factories.

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

These classes are identical except the code for one method:

procedure Utilization;

TCarrotSupermarket.Utilization works with supermarkets, TCarrotFactory.Utilization works with factories.

One identical code for Utilization I need for TCarrotSupermarket.Utilization, TTomatoSupermarket.Utilization, TPotatoSupermarket.Utilization, and another code for TCarrotFactory.Utilization, TTomatoFactory.Utilization, TPotatoFactory.Utilization.

What is the best way to write code for Utilization only twice (for supermarkets and factories) and use it in a proper classes?

7
  • 1
    Probably there is not enough information about the whole problem, but it looks like you are looking for something like the Visitor Pattern: en.wikipedia.org/wiki/Visitor_pattern Commented Mar 5, 2019 at 23:51
  • 4
    I think you have taken the wrong design approach. A Carrot should always be just a Carrot, and a Tomato should always be just a Tomato, regardless of HOW they are used. I think you need to separate your Supermarket/Factory logic out of the Vegetable classes themselves and create a separate set of VegetableUser classes for them, then you can either 1) pass a Vegetable object to a User object that will use it, and/or 2) pass a User object to a Vegetable object when the User is using it. Commented Mar 5, 2019 at 23:59
  • Thanks, I wish I could separate. In real world vegetables are different classes from TThread in legacy Win32 application. I need to use these threads in a Windows Service. And Utilization is Synchronize. Commented Mar 6, 2019 at 6:41
  • If that is the case, maybe your real world model is incorrect too. If I would program a carrot, it wouldn't be a TThread, but a simple class or even record, holding properties and methods that apply to the carrot itself. Being orange, growing, amount of beta-caroten, those are carrot things. Being sold or processed is not. So I'd have a carrot, a tomato, a factory, a supermarket, and maybe, if necessary, a separate CarrotProcessor and TomatoProcessor for the factory, which ideally share one IVegetableProcessor interface, so the factory itself doesn't have to know about specific vegetables. Commented Mar 6, 2019 at 6:51
  • I don't think any of these object is a thread by the way. They might have a thread as a helper to do their job, though. Commented Mar 6, 2019 at 6:53

2 Answers 2

1

Welcome to Pattern Design. Your case is Strategy patternn

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

Then in code:

carrotSupermarket = TCarrotSupermarket.Create(TCarrotSupermarketUtil.Create);
carrotFactory = TCarrotFactory.Create(TCarrotFactoryUtil.Create);
Sign up to request clarification or add additional context in comments.

Comments

0

Here's some pseudo code getting to a solution with using interface method resolution. (Untested, not even compiled, but it should point you in the right direction)

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)

Then when using the code it should look something like this:

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

Or otherwise using the supports routine

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

Hopefully this will help you a little. Here's a pointer to some matching documentation

2 Comments

Thanks, but I can't implement both FactoryUtilization and SuperMarketUtilization in base class. It's for code reusing for two different applications: SuperMarket and Factory
In that case, the stategy pattern (suggested by snake) is probably the best way to go as it allows to create your Utilization in different classes/units. Another alternative could be using 2 helper classes: TvegetableSupermarketHelper and TVegetableFactoryHelper=class helper for TVegetable. Be aware there are some limitations to using the helper classes. See docwiki.embarcadero.com/RADStudio/Rio/en/…

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.