views:

285

answers:

5

Hello,

Sorry for the poor title,I'm new to OOP so I don't know what is the term for what I need to do.

I have, say, 10 different Objects that inherit one Object.They have different amount and type of class members,but all of them have one property in common - Visible.

type TSObject=class(TObject);
  protected
    Visible:boolean;
  end;

type
  TObj1=class(TSObject)
  private 
    a:integer;
    ...(More members)
end;
  TObj2=class(TSObject)
  private 
    b:String;
    ...(More members)
end;

...(Other 8 objects)

For each of them I have a variable.

var Obj1:TObj1;
    Obj2:TObj2;
    Obj3:TObj3;
    ....(Other 7 objects)

Rule 1: Only one object can be initialized at a time(others have to be freed) to be visible.

For this rule I have a global variable

var CurrentVisibleObj:TSObject; //Because they all inherit TSObject

Finally there is a procedure that changes visibility.

procedure ChangeObjVisibility(newObj:TSObject);
begin
  CurrentVisibleObj.Free; //Free the old object
  CurrentVisibleObj:=newObj; //assign the new object
  CurrentVisibleObj:= ??? //Create new object
  CurrentVisibleObj.Visible:=true; //Set visibility to new object
end;

There is my problem,I don't know how to initialize it,because the derived class is unknown(TObj1,TObj2,Tobj3...Which one?).

How do I do this?

I simplified the explanation,in the project there are TFrames each having different controls and I have to set visible/not visible the same way(By leaving only one frame initialized).

Sorry again for the title,I'm very new to OOP.

A: 

You can access a base property even on a derived object because of is-a relationship. In your case, you want to have a shared state among several objects, one way to think about this is to create a manager class which will hold the objects. It will only have one object selected as the visible object. You may not need the visible property on the contained objects but this design also permits that.

Container

List MyObjects; Object MyVisibleObject;

Curtis White
A: 

You shouldn't be creating and freeing your frames every time you want to change their visibility - they should all always be initialized all the time.

David
But they are worth 30mb memory.And they are more than 10.I think its better if I create them everytime I switch between.
John
@John - I get your point. We had a VB contractor who built a large customer profile form with dozens of frames, and he's just flip the frames around to show different types of info. And that dragged the system down because it used so many resources. But that was 1997, with Pentium II processors, 256MB RAM (maybe less? I don't remember), and... Windows 3.1. IMO, being too stingy with the UI will probably lead to excessive delays while you create/destroy the UI elements. Just sayin...
Chris Thornton
@John: That's another point in favor of not allocating them and freeing them every time! You don't want to be allocating a huge chunk of memory every time your users switch frames - that'll take forever.
David
@David: Actually memory allocation and deallocation is very, very fast with FastMM. The slow part is setting up 30 MB worth of components!
Mason Wheeler
@Mason,Indeed! I tested this with two frames(if,else if).It works very smooth with FastMM,but I dont know how to make it with all frames.
John
+1  A: 

If this is about a group of TFrame controls, and you want only one of them to be visible at a time, you don't need to go freeing and creating them all the time. You could just put each frame on one page of a TPageControl, then hide the tab strip and change the visible frame using the TPageControl's ActivePage property.

Mason Wheeler
+1 It might takes a couple of extra milliseconds to start the application, but after that things go fast and smooth. Another advantage is that you the state of the frame stays unchanged when you flip back and forth. And like this it's a lot easier to maintain the GUI in the IDE.
Wouter van Nifterick
+1  A: 

There is my problem,I don't know how to initialize it,because the derived class is unknown(TObj1,TObj2,Tobj3...Which one?).

Well, there are basically two options:

  • either you pass in the type of the object to be created as a parameter to your method, e.g. the caller has to tell you what it wants to be created

  • you can determine what type to create from some other information; one easy way would be to create an object of the same type as the one you originally passed in (don't know if that makes sense in your context)

It's been a while since I've done any serious Delphi work, but I think I vaguely remember there is a way in Delphi to express a "type" that you want to have. Or maybe you're even able to create an instance of a given type based on the name of the type as a string (e.g. create an instance of TObj2 based on the string "TObj2" being passed in)

marc_s
+1,I used First idea.Wish I could ++ more. :)
John
+1  A: 

One of the first problem here is that you seem to assume you can pass an uninitialized variable to ChangeObjVisibility.

ChangeObjVisibility(Obj3);

Here, if Obj3 is nil(or worse, a dangling pointer), ChangeObjVisibility has no way to know what is the type of the object it needs to create.

One of the way you could get the class of frame to create is with an array of const, or a function with a case.

type
  TSObjectClass = class of TSObject;
const
  ObjectClasses = array[0..X] of TSObjectClass = (TObj1, TObj2, TObj3, ...)

function GetFrameclass(Index : Integer) : TSObjectClass;
begin
  Result := ObjectClasses[Index]

  OR 

  case Index of
    0 : Result := TObj1;
    1 : Result := TObj2;
    (...)
  end;
end;

That will work if the frame doesn't need any kind of special initialization.

Next, you could have a call like this one :

procedure ChangeCurrentFrame(NewFrameIndex : Integer);
var FrameClass : TSObjectclass;
    vFrame : TSObject;
begin
  FrameClass := GetFrameClass(NewFrameIndex);
  if CurrentVisibleObj.ClassType <> FrameClass then
  begin
    vFrame := FrameClass.Create(nil);
    SetCurrentFrame(vFrame);
  end;
end;

procedure SetCurrentFrame(newObj:TSObject); 
begin 
  if Assigned(CurrentVisibleObj) then
    CurrentVisibleObj.Free; //Free the old object 
  CurrentVisibleObj:=newObj; //assign the new object 
  if Assigned(CurrentVisibleObj) then
    CurrentVisibleObj.Visible:=true; //Set visibility to new object 
end; 

Here, SetCurrentFrame replace you ChangeObjVisibility(What you really do here is change the current frame, changing the visibility is just a "side effect")

Ken Bourassa
John
Passing the class to the ChangeObjVisibility create some limitations to the per-class initialization you can have. I prefer passing an already initialized object instead of passing the classtype for that reason. It makes the design a bit more flexible.
Ken Bourassa