Design patterns / virtual slot 
Author Message
 Design patterns / virtual slot

1.  Design patterns
Does anyone have experience with implementing design patterns (as described by
Gamma, Helm, Johnson and Vlissides in Design Patterns: Elements of Reusable
Object-Oriented Software) and would you want to share it?

For example the Visitor pattern (a way to implement double dispatch in
single dispatch languages) becomes trivial when using Dylan's generic functions
(i.e. multiple dispatch).  

The singleton pattern is directly supported by the singleton function in
Dylan.  

Do you know other patterns that become simple, difficult, ... to implement?

2. Virtual slot
Is there any practical use for a virtual slot?  The example in the reference
manual is not very convincing, since one can do the same thing without a
virtual slot.

If I understand the reference manual correct, all it does is automatically
generate generic functions for the getter and setter.  But it is still possible
to override this definition manually, so what is its use?



Wed, 12 Sep 2001 03:00:00 GMT  
 Design patterns / virtual slot

Quote:

> 1.  Design patterns
...
> Do you know other patterns that become simple, difficult, ... to implement?

    http://www.norvig.com/design-patterns/index.htm

  Not sure if the Design Patterns folks would be in complete agreement
  with what gets discussed there.  Additionally, I think there are
  some "features" mentioned that are in CLOS but not in Dylan (method
  combinators ) that require a tad bit more work.

Quote:
> 2. Virtual slot
> Is there any practical use for a virtual slot?  

  Yes. I think you are underrating the significance of establishing the
  protocol that the subsequent method additions must be adhere to.

  Notationally it makes explicit an element of the design.  Yes you
  could just not define any "slot" for  image.  Then just define the
  two methods (or two generics and two methods ).  However, what if someone
  else wants to edit your code two years later and you're not around?

  I think one can also use the "inherited slot specification" to
  reconfirm this virtual slot in the subclasses also. Again it has
  the advantage of communicating a design design perhaps significantly
  removed up the object hierarchy down closer to where someone might
  need to know.

  If you later decide that cached-image slots aren't necessary then image
would
  become a instance slot.  The idea is that from the outside the
  "user" of the class doesn't have to know anything about this and the change
  would be to delete the "virtual" in front and the explicitly defined
methods
  and you would be "good to go".   In some sense this ia change that has
  been planned for.  The other approach depends upon knowing what the right
  set of modifications.  

Quote:
> If I understand the reference manual correct, all it does is automatically
> generate generic functions for the getter and setter.  But it is still possible
> to override this definition manually, so what is its use?

   Subsequent methods can NOT "override" the generic's protocol.  The
   subsequent method definitions add methods that conform to the
   specified protocol.  That is what generic function definitions provide;
   a name, argument list and return value(s) that the methods must adhere to.

   In the case of the example from p.63 ("Using slots") (with a slight
   refinement...)

       define class <shape>  ( <view> )
           virtual slot  image :: <image> ;
           instance slot cached-image :: <image> , init-value: #f;
           ...
       end class;

   Here the  "image" methods must all adhere to the generic functions

       define generic image ( shape :: <shape> ) ;

       define generic image-setter ( obj :: <image> ,   shape :: <shape>,  )
;

  [ The example from the DRM would allow ANYTHING to be stored in a
    image/cached-image slot which probably isn't a good idea in practice. :-)
]

   Now if it "happens" that the two methods on the next page are compiled
   right after the class definition and there happens to be no explicitly
   defined generic already then the "right thing" will result.   If
   there is already a generic that "happens to be compatible" defined
   then "no harm no foul...... so far".    If the generic exists
   and/or is incompatible with the virtual slot then, IHMO, the class
   definition should "croak about the incompatibility".  

   Programming "in the small" the "bad outcome" likely won't happen.
However,
   Dylan is suppose to scale up.  Which mean design decisions should get
   written down in the code where possible.

   [ Actually with the generic above,  the following method.

        define method image-setter ( new-image , shape :: <shape>)
           ...
         end;

      would generate an error since it isn't congruent. It is congruent
      with the DRM's version though. ]



Wed, 12 Sep 2001 03:00:00 GMT  
 Design patterns / virtual slot

Quote:
> 1.  Design patterns
> Does anyone have experience with implementing design patterns (as
> described by Gamma, Helm, Johnson and Vlissides in Design Patterns:
> Elements of Reusable Object-Oriented Software) and would you want to
> share it?

There is a general feeling that Dylan makes a lot of the design
patterns simpler to write than traditional languages, but there is
very little hard evidence (at least that I've seen). It would be
really interesting to start collecting code snippets for each pattern
so that we could build up this evidence, it would also be useful to
discuss them amongst ourselves to see if they can be improved
further. For example, I would imagine that Dylan macros would allow us
to provide syntax to simplify the majority of patterns, it would be
great to have an archive of these somewhere. It could even make an
interesting book that would sell the power of Dylan.

Does anyone have any objections to discussing pattern implementations
here on info-dylan, I doubt that it will generate too much mail, and
it would mean that we'd see more discussions of concrete Dylan code
which would only be a good thing.

Cheers,

Andy

----------

Harlequin Inc.



Fri, 14 Sep 2001 03:00:00 GMT  
 Design patterns / virtual slot

Quote:

> There is a general feeling that Dylan makes a lot of the design
> patterns simpler to write than traditional languages, but there is
> very little hard evidence (at least that I've seen). It would be
> really interesting to start collecting code snippets for each pattern
> so that we could build up this evidence, it would also be useful to
> discuss them amongst ourselves to see if they can be improved
> further. [...]

I'm with Andy.

I once suggested, years ago by now, that someone take the "gang of 4"
patterns book and write up briefly how to do all of the patterns in
Dylan (or explain why Dylan didn't need them).  I still think that
would be a good idea, because it would be a good way to show what
Dylan can do and to increase awareness of Dylan among the many,
many programmers who are now familiar with the idea of patterns
and maybe even kind of suspect that good languages (or good
programming) and patterns somehow go together.

And, as someone who's done a fair amount of CLOS programming and
then had to use Java, my feeling is that languages such as Java
*need* patterns to a much greater extent than CLOS or Dylan.

For instance, since Java does not have full multiple-inheritance and
lacks multi-methods, some things are rather awkward to do.  It's not
clear that there's a good way to do them.  Programmer uncertainty sets
in.  Doubts crowd 'round.  Enter Patterns with a flaming sword.
(I made up the part about the flaming sword.)  Anyway, a pattern
can then say one of two things.  If you haven't thought of a way
to do what you want, the pattern can say "here's a pretty good
way to do that"; or, if you have thought of an approach but think
it's a disgusting kludge that any self-respecting language would
let you avoid, the pattern can say, reassuringly, "that's actually
though to be a pretty good approach: no one's going to laugh at
you".

The lesser need for (certain) patterns in languages such as Dylan and
Common Lisp is, I think, one of the factors behind the relative
neglect of patterns in those language communities.  But consider all
those people we hope will come to Dylan from the outside.  They may
well have heard of patterns.  Indeed, if they've programmed more
than just a little bit in Java, they'll have used patterns, because
some patterns are employed in the various Java libraries.  Patterns
might provide a good way for such programmers to get into Dylan.
Moreover, if, as I suspect, Dylan makes a number of things
significantly easier, Dylan could look pretty good to these
programmers.

-- jeff



Fri, 14 Sep 2001 03:00:00 GMT  
 Design patterns / virtual slot
....

Quote:
> I once suggested, years ago by now, that someone take the "gang of 4"
> patterns book and write up briefly how to do all of the patterns in
> Dylan (or explain why Dylan didn't need them).

  Since the "Smalltalk Companion" book of those patterns is out, that
  also might be a starting point.   I'm working on some translations
  of a more imperative algorithms book.  If "Gang of 4" isn't done by the
  time I finish that... who knows perhaps that will the next in the queue.

Quote:
> And, as someone who's done a fair amount of CLOS programming and
> then had to use Java, my feeling is that languages such as Java
> *need* patterns to a much greater extent than CLOS or Dylan.

  Well perhaps they need some of the patterns mentioned in the "Gang of 4"
  book.  However, I think there are probably patterns that occur in
CLOS/Dylan
  and others that transcend most OO languages.   I think the "Gang of 4"
  may have picked a few that are particularly clumsy in
  C++/Java just because most programmers are flagellating themselves
  with those languages.  Salve for the wounds. :-)

  Perhaps my viewpoint was skewed by too long ( or too brief ) an exposure
  to Case Based Reasoning but "patterns" seems to be one of general
  problem solving tools people use to reason.  You have generic problems
  with solutions. You have to worry about how to index ( summary
  descriptions of usefulness/applicability/utility) and adapt ( transition
from
  general solution to problem at hand) your cases/patterns to solve problems.

  So I would be surprised if over a broad class of problems CLOS/Dylan didn't
  also have patterns.  Hopefully, these would be more problem model
  driven problems than by the language being used.

--

Lyman



Fri, 14 Sep 2001 03:00:00 GMT  
 Design patterns / virtual slot

Quote:


> ....

> > And, as someone who's done a fair amount of CLOS programming and
> > then had to use Java, my feeling is that languages such as Java
> > *need* patterns to a much greater extent than CLOS or Dylan.

>   Well perhaps they need some of the patterns mentioned in the "Gang of 4"
>   book.  However, I think there are probably patterns that occur in
>   CLOS/Dylan and others that transcend most OO languages.   I think
>   the "Gang of 4" may have picked a few that are particularly clumsy in
>   C++/Java [...]

I pretty much agree with everything Lyman said in this message.

I do think patterns, in some perhaps more informal sense (than in
such places as the _Design Patterns_ book) are needed if one's to work
effectively in any language.  For instance, in Lisp there's a "pattern
of recursion" that's captured by mapcar, and it's clearly useful to be
able to recognise cases where you just have to transform a list in
that way.  The use of mixin classes in Flavors and related O-O systems
is another example of a pattern familiar to Lisp programmers and
similar sorts.  Anyway, I would certainly agree with Lyman that patterns
"seem to be one of general problem solving tools people use to reason".

But my feeling is nonetheless that with Java (and some other
languages), there's a need for certain kinds of pattern advice that's
significantly greater than when Dylan (or CLOS) is being used.

It's difficult to make this precise, or to provide a nice, knock-down
argument, but when (for instance) I'm asked about learning Java, I
usually mention that it would be useful to look at the "patterns"
literature (such as _Design Patterns_); but I don't suggest that
for, say, Common Lisp.  This is not to say _Design Patterns_
would be of no use to Common Lisp programmers, just that it has
less importance.

I think the visitor pattern is a good example.  It handles something
that's rather awkward to do in Java (but probably can be handled in a
less awkward way when there are multi-methods).  Anyway, for the Java
programmer, the pattern says "here's a way to do this" or "yes, that
looks like a very awkward way to do this, but that's how it might
actually be done by good Java programmers".  The pattern suggests an
approach that might otherwise be hard to find, and reassures one that
this is in fact a reasonable way to do things (even though it may not
look like it).

Sometimes in Java one ends up writing a big case statement, or an
elseif-sequence of instanceof calls, for a case that would be handled
by method selection in Dylan.  Or, for the visitor pattern, there
are these methods that don't do anything interesting but are needed
to make things work.  So I think there are some real opportunities
to show that things can be nicer in Dylan.

-- jeff



Fri, 14 Sep 2001 03:00:00 GMT  
 Design patterns / virtual slot

Quote:

> The lesser need for (certain) patterns in languages such as Dylan and
> Common Lisp is, I think, one of the factors behind the relative
> neglect of patterns in those language communities.  But consider all

Yes, but it doesn't fully explain it. There _are_ patterns in the way
the language is used, such as replacing switch statements by method
dispatch, most Dylan programmers are more or less conciously aware of
them, but nobody bothered to write them down yet.

Andreas

--
"It is by caffeine alone I set my mind in motion.
It is by the Beans of Java that thoughts acquire speed,
the hands acquire shaking, the shaking becomes a warning.
It is by caffeine alone I set my mind in motion." -- National Lampoon's "Doon"



Mon, 17 Sep 2001 03:00:00 GMT  
 Design patterns / virtual slot

Quote:

...
> I think the singleton pattern implementation in Dylan is quite
> interesting as I've heard mention that its implementation is trivial
> or not required due to the built in 'singleton' functionality.

  Errr,  the following does not seem to be the Singleton pattern.

  GOF p. 129

  "Clients access the signleton exclusively through the instance memeber
   function.....

   ...

    Notice that the constructor is protected.  A client that tries to
    instantiate Singleton directly will get an error at compile-time.
    This insures that only one instance ever get created."

  Translating the last part into Dylan would mean that you should get
  an error at compile (or run) time if passed to make.  In other
  words:

  define method  make ( class == < singleton-class> )
     ... invoke error ...
  end

  if anything.  Not return the sole instance.
  [ The DRM seems to say there is no "make" method for <singleton> ]

  Conceptually, one should discourage using singletons in the standard
  creation/initialization protocol.  Singletons are "different".  
  Therefore, they should have a different protocol to help distinguish
  this difference to the reader.   The normal expectation of a
  programmer would be that the creation/initializaton protocol is that
  the client/user  is to get back a "new" object each time.  [ Note if
  you use allocation pools these are still "new" objects in the
  "factory refurbished and ready to use" sense. :-)  However, they
  have been "thrown away" earlier are just being recycled. ]

  The generic function "instance" (to use the name in the GOF )
  should return the instance.  Now you can rap Dylan for that.  There doesn't
  seem to be a way to get at this sole instance with Dylan's singletons.  
  That seems to be an oversight.  You can ask a class about its superclasses.
  How come you can't ask a singleton what instance it is based on?

  I'd suggest   "singleton-instance" as the name of the access method since
  "instance" is used elsewhere for other reasons.

Quote:
> ------------------8<---------------
> define abstract class <button-gadget> (<object>)
> end class <button-gadget>;

> define abstract class <gadget-kit> (<object>)
> end class <gadget-kit>;
> define generic make-button( kit :: <gadget-kit> ) =>
>  (button :: <button-gadget>);

> define class <motif-gadget-kit> (<gadget-kit>)
> end class <motif-gadget-kit>;

> define class <motif-button> (<button-gadget>)
> end class <motif-button>;

> define method make-button( kit :: <motif-gadget-kit> ) =>
>  (button :: <button-gadget>)
>     make(<motif-button>);
> end method make-button;

I think the following variable should be in the Library  as the above
classes.
Or a intervening library which customizes the. Namely access to the "name"
should be protected.

define variable *concrete-kit* :: false-or(<gadget-kit>) = #f;

Also note that since <gadget-kit> is an abstract class unless you
define a make method an error will be signaled by default.

What you are missing is a singleton-instance method. Again this
may appear in the same library or a intervening customization library
(same location as the *concrete-kit* .

define  methd  singleton-instance ( class == <gaget-kit> )
   => (result :: <gaget-kit> )
 *concrete-kit* | (*concrete-kit* := make(<motif-gadget-kit>));
end method singleton-instance ;

Quote:
> Now I want some sort of singleton object that allows me to use
> <gadget-kit> but makes the method calls on whatever concrete
> implementation of <gadget-kit> I wanted to use. One way of doing this
> in Dylan that follows a similar implementation to the GOF book of
> singletons would be:

 With the above changes:

Quote:
> ------------------8<---------------

> define method main() => ()

     let kit = singleton-instance (<gadget-kit>);

Quote:

>    format-out("%=\n", make-button(kit));
> end method main;
> ------------------8<---------------

 The above is the GOF singleton pattern, IMHO.
 [ Well actually the singleton is suppose to be a factory of sorts.
   So I'm not sure it is good thing to make <gadget-kit> serve
   as an abstraction concept AND a factory.  That seems a little
   wierd. There would a seperate <kit-singleton> class to return
   the correct kit. ]

 Now if you would be willing to initialize the singleton instance at
 load time and  <singleton>  had the "singleton-instance" method
 Then the providing library could provide a constant

  define constant $kit = singleton( <motif-gadget-kit> );

  and just use $kit  

    format-out ("%=\n" , make-button ( singleton-instance( $kit ) );

 Of course in the above case you might just get rid of the one level of
 indirection since classes are objects.

 Instead of  <gadget-kit> being a class in the hierarchy you could do

 define class  <abstract-gadget-kit> .... end;

 define class  <motif-gaget-kit> ( <abstract-gadget-kit> ) ... end;

 Then to customize this to the particular implementation by

 define constant <gadget-kit> :: <class>  = <motif-gaget-kit>  ;

 Then you can just get rid of this singleton stuff all together.
 Just use the constant that serves as an alias to the "correct" class.

--

Lyman



Mon, 17 Sep 2001 03:00:00 GMT  
 Design patterns / virtual slot

  Drat......

Quote:
>  Now if you would be willing to initialize the singleton instance at
>  load time and  <singleton>  had the "singleton-instance" method
>  Then the providing library could provide a constant

>   define constant $kit = singleton( <motif-gadget-kit> );

better  define constant $kit = singleton ( make( <motif-gadget-kit> ) );

Quote:
>     format-out ("%=\n" , make-button ( singleton-instance( $kit ) );
...
>  Of course in the above case you might just get rid of the one level of
>  indirection since classes are objects.

  What followed was a slightly different problem.... the generic name
  as use a an abstract versus the name of convenience problem....
  That's a different problem than the singleton pattern. I've been
  thinking about that recently and it just popped into my mind.
Unfortunately,
  I wrote it down. :-(

--

Lyman



Mon, 17 Sep 2001 03:00:00 GMT  
 Design patterns / virtual slot

Quote:

>     Notice that the constructor is protected.  A client that tries to
>     instantiate Singleton directly will get an error at compile-time.
>     This insures that only one instance ever get created."

>   Translating the last part into Dylan would mean that you should get
>   an error at compile (or run) time if passed to make.  In other
>   words:

>   define method  make ( class == < singleton-class> )
>      ... invoke error ...
>   end

Actually, 'make' is allowed to return a pre-existing object. It's
standard, documented behavior. So you should implement the singleton pattern
as:

  define variable *the-only-foo-object* :: false-or(<foo>) = #f;

  define class <foo> (<object>)
  end;

  define method make
      (class == <foo>, #next next-method, #rest keys, #key, #all-keys)
   => (object :: <foo>)
    if (~ *the-only-foo-object*)
      // Not threadsafe!
      *the-only-foo-object* := next-method();
    end if;
    *the-only-foo-object*;
  end method;

You can implement factories in a similar fashion.

Cheers,
Eric



Mon, 17 Sep 2001 03:00:00 GMT  
 Design patterns / virtual slot

Quote:

> Does anyone have any objections to discussing pattern implementations
> here on info-dylan, I doubt that it will generate too much mail, and
> it would mean that we'd see more discussions of concrete Dylan code
> which would only be a good thing.

I think this would be a good thing. Discussions about concrete Dylan
code is definitely a good thing and talking about implementing common
design patterns may reveal some of the common Dylan idioms to those of
us who haven't been using Dylan for very long. That said, here's a few
thoughts on the singleton pattern.

I think the singleton pattern implementation in Dylan is quite
interesting as I've heard mention that its implementation is trivial
or not required due to the built in 'singleton' functionality.

I'm hoping this article will provoke some discussion on how to
implement singleton objects in Dylan as defined by the 'gang of four'
design patterns book (called GOF from here on) and how this may be
different to the 'singleton' concept that exists in Dylan.

I've used the singleton pattern when I have a class that I only want
one instance of in my application. I use this mainly for access to
system wide resources or factory style objects. I want multiple parts
of my application to be able to use the singleton object without
having to use a global variable mainly for the benefits as described
in GOF.

As an example, I might have a 'GadgetKit' that returns different
gadget objects. Different imlpementations of GadgetKit exist for
different window styles (Motif, MS-Windows look, Java look, etc). I
want my application to be able to use methods on a singleton GadgetKit
object with the actual GadgetKit implementation class having been
created previously as one of the different concrete
implementations. The rest of my application doesn't care and I can
swap different kits by changing the single place in the code that
creates the concrete GadgetKit implementation.

Similar functionality to this is probably my most common use of the
Singleton pattern in other languages (ie. access to system wide
resources).

So a Dylan GadgetKit implementation might look something like (I'm
primarily a C++ programmer so this may be very C++ looking - any
other ways of doing it are certainly appreciated):

------------------8<---------------
define abstract class <button-gadget> (<object>)
end class <button-gadget>;

define abstract class <gadget-kit> (<object>)
end class <gadget-kit>;
define generic make-button( kit :: <gadget-kit> ) =>
 (button :: <button-gadget>);

define class <motif-gadget-kit> (<gadget-kit>)
end class <motif-gadget-kit>;

define class <motif-button> (<button-gadget>)
end class <motif-button>;

define method make-button( kit :: <motif-gadget-kit> ) =>
 (button :: <button-gadget>)
    make(<motif-button>);
end method make-button;
------------------8<---------------

Now I want some sort of singleton object that allows me to use
<gadget-kit> but makes the method calls on whatever concrete
implementation of <gadget-kit> I wanted to use. One way of doing this
in Dylan that follows a similar implementation to the GOF book of
singletons would be:

------------------8<---------------
define variable *concrete-kit* :: false-or(<gadget-kit>) = #f;

define method make( class == <gadget-kit>, #key) => (result :: <gadget-kit>)
  *concrete-kit* | (*concrete-kit* := make(<motif-gadget-kit>));
end method make;

define method main() => ()
   let kit = make(<gadget-kit>);

   format-out("%=\n", make-button(kit));
end method main;
------------------8<---------------

This would ensure that any call to make(<gadget-kit>) would return the
single instance of the concrete class.

If the 'make' method above was placed in a DLL (or shared library) for
each different concrete implentation then that DLL could be loaded at
runtime allowing different kits to be used without rebuilding the
application.

I think this implements the basic singleton pattern as defined by GOF
but it does look a lot like how it would be implemented in C++ or
Java. This is probably due to my background being those languages. It
is nicer in that you don't need to call some arbitarily named
'Instance' method or static object. You get to use the normal Dylan
method of creating objects - 'make'.

Perhaps there is a macro that can be done to create some sort of
'define singleton-pattern' that automatically creates the 'make' as
above, etc. Any ideas?

I looked at the Dylan 'singleton' function and other language support
but this seems to be for creating unique types for individual objects
and I can't see how it fits into implementing the singleton
pattern. This seems to be opposite of what I've seen mentioned a few
times that Dylan makes the singleton pattern not so useful due to
language support. Is there a more common Dylan idiom for implementing
singleton objects like the above?

Chris.



Tue, 18 Sep 2001 03:00:00 GMT  
 Design patterns / virtual slot

Quote:
>   Errr,  the following does not seem to be the Singleton pattern.

I can see your point. I thought about using an instance member
function but I used make() as that seemed to be a fairly common way of
ensuring only one instance of an object existed in Dylan. In
particular a couple of the Harlequin examples that ship with Harlequin
Dylan do it that way. The corba Chat example has the chat server as a
class that only one instance can be created, that instance returned
via make in a similar manner to what I outlined.

I do agree that it can be a bit confusing to use a creation protocol
to return the single instance.

Quote:
>   The generic function "instance" (to use the name in the GOF )
>   should return the instance.  Now you can rap Dylan for that.
>   There doesn't seem to be a way to get at this sole instance with
>   Dylan's singletons.  That seems to be an oversight.  You can ask a
>   class about its superclasses.  How come you can't ask a singleton
>   what instance it is based on?

Browsing <singelton> in Harlequin Dylan shows a slot
'singleton-object' which appears to provide access to the instance. It
doesn't appear to be exported so is not useable unfortunately.

Quote:

>  The above is the GOF singleton pattern, IMHO.  [ Well actually the
>  singleton is suppose to be a factory of sorts.  So I'm not sure it
>  is good thing to make <gadget-kit> serve as an abstraction concept
>  AND a factory.  That seems a little wierd. There would a seperate
>  <kit-singleton> class to return the correct kit. ]

The example in GOF page 132 - the MazeFactory - does a similar type
of thing, using MazeFactory as an abstract concept and a factory. In
that example the instance method has to be changed everytime a new
subclass of MazeFactory is defined. At least in Dylan this is made
easier to extend - using first class types perhaps:

define abstract class <maze-factory>
end class <maze-factory>;

define class <bombed-maze-factory>
end class <bombed-maze-factory>;

define class <enchanted-maze-factory>
end class <enchanted-maze-factory>;

define variable *maze-type* = <enchanted-maze-factory>;

define  method  singleton-instance ( class == <maze-factory> )
   => (result :: <maze-factory> )
  *concrete-factory* | (*concrete-factory* := make(*maze-type*));
end method singleton-instance ;

Quote:
> Then to customize this to the particular implementation by define
> constant <gadget-kit> :: <class> = <motif-gaget-kit> ;

>  Then you can just get rid of this singleton stuff all together.
>  Just use the constant that serves as an alias to the "correct"
>  class.

This doesn't provide a single instance of the kit though. It is a nice
way of defining what concrete kit to produce and then use your
singleton-instance method to get at the single instance.

I guess you can get rid of the singleon instance by specialising the
methods based upon the type. Something like:

------------------8<---------------
define method make-button( kit-type == <motif-gadget-kit> ) =>
 (button :: <button-gadget>)
    make(<motif-button>);
end method make-button;

define method make-button ( kit-type == <java-gadget-kit> ) =>
 (button :: <button-gadget>)
    make(<java-button>);
end method make-button;
-------------------8<---------------

Chris.



Tue, 18 Sep 2001 03:00:00 GMT  
 Design patterns / virtual slot

Quote:
> Actually, 'make' is allowed to return a pre-existing object. It's
> standard, documented behavior. So you should implement the singleton
> pattern as:

>   define variable *the-only-foo-object* :: false-or(<foo>) = #f;

>   define class <foo> (<object>)
>   end;

>   define method make
>       (class == <foo>, #next next-method, #rest keys, #key, #all-keys)
>    => (object :: <foo>)
>     if (~ *the-only-foo-object*)
>       // Not threadsafe!
>       *the-only-foo-object* := next-method();
>     end if;
>     *the-only-foo-object*;
>   end method;

This does work, I don't like it very much though! One problem is that
it is quite a lot of code, and two is that you have to create a
variable that is constant almost immediately!

I would prefer to have an open abstract class that stores the only
instance in an each-subclass slot. Then you would be able to create
singletons with code like:

  define class <foo> (<singleton-class>)
  end class <foo>;

  make(<foo>);

Quite a bit nicer, don't you think! The name <singleton-class> isn't
really right, though, since it isn't a subclass of <class>. Even
better IMO would be to define a new subclass of <class> to handle
this, we could add a new class modifier like:

  define singleton class <foo> (<object>)
  end class <foo>;

  make(<foo>);

Here's an attempt at defining <singleton-class>, but I realize it
doesn't work because you can't access the class slot from the class
(or can you, it seems a large oversight if you can't).

  define open abstract class <singleton-class> (<object>)
    each-subclass slot
      singleton-instance :: false-or(subclass(<singleton-class>)) = #f;
  end class <singleton-class>;

  define method make
      (class :: subclass(<singleton-class>), #key)
   => (object :: <singleton-class>)
    class.singleton-instance
      | begin
          class.singleton-instance := next-method()
        end
  end method make;

Just some half-formed thoughts...

Andy

----------

Harlequin Inc.



Tue, 18 Sep 2001 03:00:00 GMT  
 Design patterns / virtual slot

Quote:
> Now I want some sort of singleton object that allows me to use
> <gadget-kit> but makes the method calls on whatever concrete
> implementation of <gadget-kit> I wanted to use. One way of doing
> this in Dylan that follows a similar implementation to the GOF book
> of singletons would be:

> ------------------8<---------------
> define variable *concrete-kit* :: false-or(<gadget-kit>) = #f;

> define method make( class == <gadget-kit>, #key) => (result :: <gadget-kit>)
>   *concrete-kit* | (*concrete-kit* := make(<motif-gadget-kit>));
> end method make;

> define method main() => ()
>    let kit = make(<gadget-kit>);

>    format-out("%=\n", make-button(kit));
> end method main;
> ------------------8<---------------

> This would ensure that any call to make(<gadget-kit>) would return
> the single instance of the concrete class.

One problem with this is that someone could still call
make(<motif-gadget-kit>) and end up with two instances. I think you
would want to make sure that make on <motif-gadget-kit> would return
the same object. Also you are dropping any keyword arguments that
might want to be passed on.

Maybe you should replace the single make method above with the
following two:

  define method make
      (class == <gadget-kit>, #rest args, #key)
   => (result :: <gadget-kit>)
    apply(make, <motif-gadget-kit>, args)
  end method make;

  define method make
      (class == <motif-gadget-kit>, #key)
   => (result :: <motif-gadget-kit>)
    *concrete-kit* | (*concrete-kit* := next-method());
  end method make;

Also, this is a slightly {*filter*} example if <gadget-kit> is an open
class in one library, and <motif-gadget-kit> is implemented in a
separate DLL as you imply you might want to do. The make method on
singleton(<gadget-kit>) will be in the Motif DLL, but it adds
functionality visible to libraries that don't use the Motif DLL. In
Harlequin Dylan we call this a 'sideways' declaration, and you will
get a warning about it.

It is considered bad style to do this, because those other libraries
might change behavior depending on whether your DLL is loaded. Your
program will also crash if it tries to load another DLL that also
defines a make method on <gadget-kit> (or maybe it will silently
replace the old concrete class with the new one).

So I'd recommend that instead you add an explicit registering of the
concrete class, something like the following. It also has the
advantage of providing very clear diagnostics when the programmer uses
the toolkit incorrectly (loads two concrete subclasses, or forgets to
load any).

  ------------------------------8<------------------------------
  Module: gadget-kit

  define open abstract class <gadget-kit> ...;

  define variable
    *concrete-kit-class* :: false-or(subclass(<gadget-kit>)) = #f;

  define function register-gadget-kit-class
      (class :: subclass(<gadget-kit>)) => ()
    if (*concrete-kit-class*)
      error("Already installed a concrete gadget kit!")
    else
      *concrete-kit-class* := class
    end
  end function register-gadget-kit-class;

  define method make
      (class == <gadget-kit>, #rest args, #key)
   => (result :: <gadget-kit>)
    if (*concrete-kit-class*)
      apply(make, *concrete-kit-class*, args)
    else
      error("No concrete gadget kit loaded!")
    end
  end method make;
  ------------------------------8<------------------------------

  ------------------------------8<------------------------------
  Module: motif-gadget-kit

  define class <motif-gadget-kit> (<gadget-kit>) ...;

  define variable *concrete-kit* :: false-or(<motif-gadget-kit>) = #f;

  define method make
      (class == <motif-gadget-kit>, #key)
   => (result :: <motif-gadget-kit>)
    *concrete-kit* | (*concrete-kit* := next-method());
  end method make;

  register-gadget-kit-class(<motif-gadget-kit>);
  ------------------------------8<------------------------------

This is all untested, but it is pretty much what I've always wanted to
do in DUIM (right now we use a sideways method!). DUIM is different in
that we want to allow multiple concrete <frame-manager> classes (our
equivalent of <gadget-kit>), so we really have to do what I describe
to allow them to co-exist peacefully.

Hope this helps,

Andy

----------

Harlequin Inc.



Tue, 18 Sep 2001 03:00:00 GMT  
 
 [ 55 post ]  Go to page: [1] [2] [3] [4]

 Relevant Pages 

1. Design Patterns for HW design ?

2. Design Patterns for HW design ?

3. Virtual Slots

4. Virtual Slots

5. Making a virtual slots metaclass

6. Signal/slot pattern in ruby

7. ANNOUNCE: virtual proxy pattern posted to ACM archives

8. Structure slot-init accessing previous slot

9. Slots of a slot definition metaobject

10. Need a Smalltalk virtual machine design knowledge

11. OOPSLA'99 VM Workshop: Simplicity, Performance and Portability in Virtual Machine Design

12. RESEARCH: Virtual Actors Behaviour Design?

 

 
Powered by phpBB® Forum Software