[Xotcl] parameters to keep Class objects

Stefan Sobernig stefan.sobernig at wu.ac.at
Fri Sep 15 12:07:58 CEST 2023


Hi Maksym!

> Basically I want something like *Factory Method* pattern.

This clarifies your request! Thanks!

Looking at your design snippet, I would have some suggestions for 
improvement.

 From what I see, your design is not really realising the advantages of 
the FACTORY METHOD (e.g., adding new types of sessions *without* having 
to touch the factory method and the switch in there), nor is it very 
robust because of re-using the NX constructor "init" as the factory method.

o Robustness: Re-using "init" that way is not a good choice in the 
context of NaviServer, because unless you are careful, NaviServer will 
run "init" just once (when building the blueprint script). When shipping 
the script to the interpreters of connection threads, init will not be 
run again (-noinit is added). In addition, autonamed NX objects (those 
created via calling "new") are also not exported into the blueprint 
script. Hence, you experience the "${:storageObj} doesn't exist" issue 
in the connection threads.

o FACTORY METHOD: The by-the-book design of a FM implementation creates 
two lines of concepts: session factories and sessions. For both lines, 
two class hierarchies are typically created, like:

namespace eval ::oodz {

   nx::Class create Session {
     :public method save {args} {;}
   }
   nx::Class create FileSession -superclasses Session
   nx::Class create DBSession -superclasses Session


   nx::Class create SessionFactory {

     :public method createSession {} {
       error "[:info class]: abstract method, use subclass!"
     }

     :create ::sessionFactory
   }

   nx::Class create FileSessionFactory -superclasses SessionFactory {
     :public method createSession {} {
       return [FileSession new]
     }
   }

   nx::Class create DBSessionFactory -superclasses SessionFactory {
     :public method createSession {} {
       return [DBSession new]
     }
   }

   namespace export Session DBSessionFactory DBSession 
FileSessionFactory FileSession
}

This way, you could call:

namespace import ::oodz::*
set s [[DBSessionFactory new] createSession]
$s save

This is a lot of design boilerplate, I prefer the following (using NX/ 
XOTcl idioms incl. a metaclass):

namespace eval ::oodz {

   nx::Class create SessionClass -superclasses ::nx::Class

   SessionClass create Session {
     :public method save {args} {;}
   }
   SessionClass create file -superclasses Session
   SessionClass create db -superclasses Session


   nx::Class create SessionFactory {

     :public method createSession {-persist_type:class,type=SessionClass} {
       return [$persist_type new]
     }

     :create sessionFactory
   }

   namespace export sessionFactory file db
}

Then:

namespace import -force ::oodz::*
set s [sessionFactory createSession -persist_type db]
$s save

Some remarks:

- "init" is not used!

- session-class names are used as first-class symbols of your factory 
method (no need to maintain yet another mapping: "file" <-> 
"FileSession" or similar)

- You can easily add new session types, by creating new SessionClass 
instances;

- Clearly, as always, there are alternatives (e.g., maintain a 
dictionary of persist_types symbols and session classes).

HTH, Stefan

> happening right now is that I am initializing my Session class. It works 
> fine but after some time I'm getting an error that ${:storageObj} doesnt 
> exist.
> In your example I need to know my type of the class before. Sorry, maybe 
> I understood wrong. If you have a simple example of illustrating factory 
> method I would be grateful.
> Thank you
> 
> On Tue, Sep 12, 2023 at 7:02 AM Stefan Sobernig 
> <stefan.sobernig at wu.ac.at <mailto:stefan.sobernig at wu.ac.at>> wrote:
> 
>     Hi Maksym!
> 
>     I would recommend using a "property", watch:
> 
>     package req nx
> 
>     nx::Class create a {
>           :public method hmmm {} {
>              return "Hmmmm!!!"
>           }
>     }
>     nx::Class create b {
>           :property -accessor public aObj:object,type=::a
> 
>           :public method saysmth {} {
>              puts "Hello!? [${:aObj} hmmm]"
>           }
>     }
> 
> 
>     # in init.tcl / created once
>     ::b create bobj
> 
>     # somewhere else (later):
> 
>     ::bobj aObj set [::a new]
>     ::bobj saysmth
> 
>     What do you think?
> 
>     HTH, Stefan
> 
> 
>      > Hello, can I use parameters to keep Class objects? Like this:
>      >
>      > package require nx
>      >>
>      >> nx::Class create a {
>      >> :public method hmmm {} {
>      >> return "Hmmmm!!!"
>      >> }
>      >> }
>      >>
>      >> nx::Class create b {
>      >> :method init {} {
>      >> set :aObj [a new]
>      >> }
>      >>
>      >> :public method saysmth {} {
>      >> puts "Hello!? [${:aObj} hmmm]"
>      >> }
>      >> }
>      >>
>      >> ::b create bobj
>      >> ::bobj saysmth
>      >>
>      > Is it possible that it will not be set if I create my "bobj" from
>     init.tcl
>      > in Naviserver at startup?
>      >
>      > Thank you in advance
>      > _______________________________________________
>      > Xotcl mailing list
>      > Xotcl at alice.wu.ac.at <mailto:Xotcl at alice.wu.ac.at>
>      > http://alice.wu.ac.at/mailman/listinfo/xotcl
>     <http://alice.wu.ac.at/mailman/listinfo/xotcl>
> 
>     _______________________________________________
>     Xotcl mailing list
>     Xotcl at alice.wu.ac.at <mailto:Xotcl at alice.wu.ac.at>
>     http://alice.wu.ac.at/mailman/listinfo/xotcl
>     <http://alice.wu.ac.at/mailman/listinfo/xotcl>
> 




More information about the Xotcl mailing list