tags:

views:

156

answers:

5

Why in the code below, do I get the "Failed" message rather than "Succeeded"

Background: I like to have class procedures that instantiate their owner object, do something, and then free it.

However, this approach doesn't work if I have a descendant object:

Any suggestions on how to provide class procedures in a base class that can be called as a child? Am I thinking about this wrongly?

Type
  TBase = class(TObject)
    Protected
       Procedure Proc1; Virtual;
    Public
       Class Procedure MyClassProc;
  end;

  Class Procedure TBase.MyClassProc;
  Var
    Base: TBase;
  begin
    Base := TBase.Create;
    Base.Proc1;
    Base.Free;
  end;

  Procedure TBase.Proc1;
  begin
    Assert(FALSE, 'Failed');
  end;

type
   TChild = class(TBase)
   protected
      Procedure Proc1; Override;
   end;

   Procedure TChild.Proc1;
   begin
     ShowMessage('Succeeded');
   end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  TChild.MyClassProc;
end;
+1  A: 

Strip everything down to the bare minimum, and you will see that you only ever create a TBase instance, so consequently only TBase.Proc1() will ever be called. If you want to have TChild.Proc1() be called you need to create a TChild instance and let polymorphism work its magic.

There could however be better ways to achieve your goal (whatever it is) than to have a class method create an object instance to do something. Maybe you should clarify your question.

mghie
@mghie: Hi! In early days I got a question like that! Let me explain: I Made a Base form to list some especials properties in the code inspect, with various inherited forms, one of that properties was the kind of form which was staticaly recorded in the .FRM file and I needed no know what kind of form was it to register it in my custon form factory. The logic of registering was in the Base Form's class for share with his childs. The only one way I fould to retrieve the form kind was instantiate it and then get it from the form "object". Regards.
Gedean Dias
+1  A: 

Here it is

Add

TBase = class;
TBaseClass = class of TBase;

TBase = class(TObject)
protected
  class function GetBaseClass: TBaseClass; virtual;

function TBase.GetBaseClass: TBaseClass;
begin
  Result := TBase;
end;

TChild = class(TBase)
protected
  class function GetBaseClass: TBaseClass; override;

function TChild.GetBaseClass: TBaseClass;
begin
  Result := TChild;
end;

Change

from

Base := TBase.Create;

to

Base := GetBaseClass.Create;

Enjoy your work

Cheer

A Pham

That's an obvious workaround, but it still begs the question: why would one do it this way? Why the class method, why not a free function? Why have a class hierarchy **at all**?
mghie
@mghie - I have used a model similar to this in the past, but with a class registry to determine what class to create at runtime, or to "clone" the current class.
skamradt
@skamradt: Polymorphism and class methods don't go together for me. if an instance is to be created anyway, way not pass it around instead of the class? As for *Clone()* - this would not need to be a class method, as an instance is there to be cloned?
mghie
+4  A: 

You can do it easily with meta-programmation! Just change "TBase.Create" to "Self.Create" "self" represents the current class, it doesn't metter if is a base o a child class.

Type
  TBase = class(TObject)
    Protected
       Procedure Proc1; Virtual;
    Public
       Class Procedure MyClassProc;
  end;

  Class Procedure TBase.MyClassProc;
  Var
    MyObject: TBase;
  begin
    // MyObject := TBase.Create;
    MyObject := Self.Create; // The Magic goes here, self is the class that's calling this method, in this case, TChild }
    MyObject.Proc1;
    MyObject.Free;
  end;

  Procedure TBase.Proc1;
  begin
    Assert(FALSE, 'Failed');
  end;

type
   TChild = class(TBase)
   protected
      Procedure Proc1; Override;
   end;

   Procedure TChild.Proc1;
   begin
     ShowMessage('Succeeded');
   end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  TChild.MyClassProc;
end;
Gedean Dias
+1. I still don't like the whole idea, but at least this comes with the minimum of additional code, and has no danger of screwing up by forgetting to provide the overridden helper function.
mghie
A: 

Thanks, mghie.

Why I want a class procedure like the one that I showed:

When I need some simple task done by a class, it's a hassle to have to instantiate the class just to make the one call. (1. Declare the variable. 2. Create the class. 3. Make the call 4. Free the class. --> that's 4+ lines of code make a single call.

So, I created a class procedure that does all the work. This works fine, except in the common scenario when there is a descendant object...

So I'll have to add to my child code that is almost a mirror of the base class's:

class procedure TChild.MyChildClassProc;
begin
  Child := TChild.Create;
  Child.Proc1;
  Child.Free;
end;

and call it explicitly with

TChild.MyChildclassProc;

Because this is the kind of gratuitous code duplication and renaming is just the kind of thing that I thought (in my inexperience) that OO was supposed to avoid, I thought I'd ask here.

Thanks for your reply above.

Tom

Tom
If you have a need for an object instance, don't hide it. Make it as explicit for the person reading the code as possible (it could be you, a few months later!). Use the smart edit features of the IDE, and it won't even take longer to write.
mghie
This makes me wonder why there's a class at all, especially an *instance* of a class. If a non-instance method with no parameters is sufficient for initiating the task, then what's the point of the instance?
Rob Kennedy
A: 

I'm having some trouble getting SO to allow me to add comments or flag answers (I just registered), so I'm doing it here.

Gedean Dias's answer is exactly what I needed.

MyObject := Self.Create; // The Magic goes here, self is the class that's calling this method, in this case, TChild }

I don't quite understand the basis of the "why would you want to do this?" comments. Maybe, being relatively new, I'm missing something?

Here's an example: suppose I had some class that did all sorts of directory operations: browsers, copy all, rename all with confirmation, dirlist-type operations, etc. Suppose that the class also contains a simple method like "Copy file with confirmation." The method is part of the class because it's function is related to the class and it uses some of the class's private functionality and the class uses it.

Why would I want to go through the "gymnastics" of instantiating the class just to make a one-line "Copy File with confirmation?" call?

Doesn't it make sense to create a class procedure of the sort I have, which instantiates, calls, and frees?

I'm not trying to be arguementative or prove anything.

Is there some aspect of OO design that I'm missing that makes this not good coding?

Thanks all. Sorry to belabor an issue on my first posting here. I'll behave! I promise! :-)

Tom
Where in your example is the need to create an instance of an object? If you create a class merely to group functions that belong together, then *all* methods could be class methods, no? Everything that can be implemented without fields can (probably should) be a class method. This even makes the class thread-safe. Similarly, where is the need to have class inheritance for such classes?
mghie
My example fell short in the areas you describe, mghie. I can envision (in fact, that's was precipitated my original post) a class that has deeper functionality, uses fields, and yet has some functionality that a simple class method the temporarily instantiates the object is called for. Anyway, thanks so much for taking the time to discuss this. I think we've probably kicked it around enough that we can move on. Thanks again!
Tom
If the class has fields, then to my mind, that implies "state" held by the object, so again, I'm with the rest of the team who are having trouble understanding why you only want to temporarily instantiate the object.If you do want to persist, then you could maybe look into the Singleton pattern.
Conor Boyd
See my comment to mghie's answer
Gedean Dias