Re: [Vala] using named constructors with runtime type determination



On 2 February 2011 00:10, James Moschou <james moschou gmail com> wrote:
Hello,

I have an abstract class, which has a named constructor and would like
to be able to create instances of concrete subclasses by passing the
type to a factory method. Specifically:

abstract class Document : Object {
 Document.with_name (string name) { ... }
}

class TextDocument : Document {
 TextDocument.with_name (string name) { ... }
}

Document get_document_with_name (string name, Type document_type) {
 // Something here ...
}

// Usage
TextDocument text_document = (TextDocument) get_document_with_name
(name, typeof (TextDocument));


I have a feeling such a thing is possible using GObject constructor
properties and the construct block, but I'm not sure how. I've been
avoiding doing object construction the GObject way up until now, and
would prefer to keep doing so if possible as it is simpler to
understand.

Also this factory method is just for a special case, i.e. the normal
way to create documents is actually using the new keyword. So if I
have to use GObject construction, I would prefer it to be alongside
the normal constructors; is this possible? The documentation I've read
seems to assume it's one or the other.

Regards,
James


I think I've managed to solve this in a robust, if a bit convoluted
way. It involved delegating the initialisation code out of the
constructors to virtual init... methods. Utilising virtual methods is
what allows the factory method to work.

Then I used the concept of a "designated initialiser" to ensure the
classes would chain up in the correct order, without weird behaviour
happening with overridden virtual methods.

http://developer.apple.com/library/mac/#documentation/cocoa/conceptual/ObjectiveC/Articles/ocAllocInit.html
(about half way down)

Altogether:

abstract class Document : Object {
  Document () {
    init ();
  }
  Document.with_name (string name) {
    init_with_name (name);
  }
  // init is the designated initaliser
  virtual void init () { ... }
  virtual void init_with_name (string name) {
    init ();
    ...
  }
}

class TextDocument : Document {
  TextDocument () {
    base ();
  }
  TextDocument.with_name (string name) {
    base.with_name ();
  }
  // init here is also the designated initialiser
  // so it chains up to the base designated initialiser
  override void init () {
    base.init ();
  }
  override void init_with_name (string name) {
    init ();
    ...
  }
}

Document get_document_with_name (string name, Type document_type) {
  Document doc = (Document) Object.new (document_type);
  doc.init_with_name (name);
  return doc;
}


This way initialisers get called:
1. Document.init ()
2. TextDocument.init ()
3. TextDocument.init_with_name ()

whether using Object.new() or normal construction.

I know this probably seems like I'm trying to replicate another
programming environment's paradigm where it doesn't belong, but I
can't use construct properties, because that forces you to store the
passed arguments as properties. I might choose to interpret 'name' as
a filepath and store the File object, or just process 'name' without
storing anything, etc.

Regards
James



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]