Proper OOP? How?

Sorry about the title, couldn't think of anything better!

I have an app that responds to commands with data on the serial port.  I have a set of command objects that correspond to real commands.  Each one has a LoadFromStream method that gets the data from the serial port decoder (it does unencrypting and checksums etc).

Each command object is descended from TCustomCommand which has an abstract virtual LoadFromStream method.

I want to read the first byte of the stream which designates the command type, then create a command object:

var
  obj : TCustomCommand;
  Com : Byte;
begin
  S.Read(Com,1);
  case Com of
     1 : Obj:=TCommand1.Create;
     2 : Obj:=TCommand2.Create;
    etc.
  end;
  If Assigned(Obj) then
    Obj.LoadFromStream(S);

Now there's the tricky bit....the last line.  Does TCommand1.LoadFromStream get called?  Or does the compiler link to the declared object - ie TCustomCommand.LoadFromStream?

Can I use (Obj as Obj.ClassType).LoadFromStream?

None of the above attempts acutally compile and run.  Every time it tries to call an abstract method (ie TCustomCommand.LoadFromStream) or with Obj as Obj.ClassType it tries to call TObject.LoadFromStream at compile time, and fails cos TObject doesn't have the method.

HELP!!!!
This must be possible - it's one of the great niceties of OOP, isn't it?
  S.Read(
LVL 1
bcrotazAsked:
Who is Participating?

[Product update] Infrastructure Analysis Tool is now available with Business Accounts.Learn More

x
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

mirek071497Commented:
Tell me how You declare classes, or more abaut you'r code.

I Think so this must be as in my example then you can call Obj.LoadFromStream(..);

 TCustomCommand = class
  ....
  procedure LoadFromStream(..); virtual; abstract;
  ...
  end;

  TCommand1 = class(TCustomCommand)
  ...
  procedure LoadFromStream(...); override;
  ...
  end;

  of course LoadFromStream can be in private or protected only if you call this within module. For the first time try this procedure in public.
0
icampbe1Commented:
The quickest way with your current setup would be to CASE again.

ie. at the end, do:

IF Obj <> NIL THEN
CASE Com OF
  1:  TCommand1(Obj).LoadFromStream(S);
  2:  TCommand2(Obj).LoadFromStream(S);
  etc..
END;
0
bcrotazAuthor Commented:
case seems messy.
I have two procedures now. one works, the other doesn't....

Procedure 1
var
  Obj: TCustomCommand
begin
  case com of
     1 : Obj:=TCommand1.Create;
     2 : Obj:=TCommand2.Create;   etc
  end;
  Obj.LoadFromStream(S);
end;

That works.  The correct descendant method is called.

However, compare to this:

As above, but instead of last line:
DoLoad(Obj);


procedure DoLoad(Obj: TCustomCommand);
begin
  Obj.LoadFromStream(S);
end;

This one calls TCustomCommand.LoadFromStream;
0
PMI ACP® Project Management

Prepare for the PMI Agile Certified Practitioner (PMI-ACP)® exam, which formally recognizes your knowledge of agile principles and your skill with agile techniques.

bcrotazAuthor Commented:
It must be possible to do this by OOP methods - I shouldn't have to iterate through all my object types to find which one I have...
0
mirek071497Commented:
As i write some day's ago you probably mad mistake in setting procedures to protected statement.

This example works - this is OOP and must works (i was tested this)

type
  TAbstractClass = class
    procedure   LoadFStream; virtual; abstract;
  end;

  TTestClass1 = class( TAbstractClass )
    procedure   LoadFStream; override;
  end;

  TTestClass2 = class( TAbstractClass )
    procedure   LoadFStream; override;
  end;

implementation

procedure TTestClass1.LoadFStream;
begin
  ShowMessage( 'Test Class 1' );
end;

procedure TTestClass2.LoadFStream;
begin
  ShowMessage( 'Test Class 2' );
end;

procedure DoLoad( cc : TAbstractClass );
begin
  cc.LoadFStream;
end;

{ on form i create 2 buttons }

procedure TForm1.Button1Click(Sender: TObject);
var
  t1, t2 : TAbstractClass;
begin
  t1 := TTestClass1.Create;
  t2 := TTestClass2.Create;
  t1.LoadFStream;
  t2.LoadFSTream;
  t1.Free;
  t2.Free;
end;

procedure TForm1.Button2Click(Sender: TObject);
var
  t1, t2 : TAbstractClass;
begin
  t1 := TTestClass1.Create;
  t2 := TTestClass2.Create;
  DoLoad( t1 );
  DoLoad( t2 );
  t1.Free;
  t2.Free;
end;

All procedures call good class - try !

but when Abstract class have constructor in public and next classes have constructor in protected then if you use them from other unit you have big problem. When you call TTestClass1.Create you call TAbstractClass.Create because procedures in protected statement is invisible in other units.

0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
mirek071497Commented:
Hi
Do you understand my answer ?
Do you need any help ?
0
bcrotazAuthor Commented:
Thanks.
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Delphi

From novice to tech pro — start learning today.