Beefy Boxes and Bandwidth Generously Provided by pair Networks
Welcome to the Monastery
 
PerlMonks  

Yet Another Perl Object Model (Inside Out Objects)

by demerphq (Chancellor)
on Dec 15, 2002 at 00:38 UTC ( [id://219924]=perlmeditation: print w/replies, xml ) Need Help??

Recently a number of monks have been discussing Perl OO and perl OO object models. One thing that I've found a interesting is the number of approaches that are out there. All of them attempt to resolve problems (or perceived problems) in the perl approach to OO. A common problem that is attempted to be solved is that of internal attribute clashes in inherited modules. I personally find this a little strange as I tend to not find this to be a noteworthy issue in my day to day use of objects in perl. Now perhaps I am not doing the kind of heavy inheritance that would cause this type of issue to be common enough for me to call it a problem, but I think that sometimes its because I look at it in a different way.

In Perl very rarely do you not have access to the perl code that you are inheriting. (The only example I can think of is a pure XS object, and then I wonder if it matters.) So its not particularly difficult to see if you are walking on attributes. You can review the code (either in a debugger or with the appropriate calls to a serialization module and other types of debugging) and see what attributes to be careful of. If you are paranoid you can use special prefixes on hash attributes (like the __PACKAGE__ name), or other tricks. But tricks more often then not seem to just lead to yet more tricks to do other things that used to be easy. ( Such as the contortions required to serialize an inside out object. See below).

Of course accidents happen, and tools like Tie::SecureHash or other techniques can help to avoid them, but generally speaking I tend to think that a rigourous approach to testing is sufficient to make these tools overkill most of the time. If you've mistyped a hash key somewhere, or negligently walked over a parent classes attribute then your test cases should raise enough alarms that identifying the cause of the error is not difficult.

As you can see, i'm not particularly inclined to depart from the plain vanila blessed array or hashref type objects. Despite this I think its worthwhile exploring these alternate models so as to understand what pitfalls they hope to avoid and which they don't.

One approach that i'd like to consider in more detail is Abigail- II's concept of inside-out objects. I think that this is an interesting idea as it does make subclassing quite easy. I don't think I would tend to use it much but it is interesting enough that I played with it for a while today. One aspect that caught my attentio was how do to serialize an object constructed like this. Its not straight forward at all. :-) I played around with it for a while and in the end the solution I came up with involves three aspects that id like to mention breifly, before i present the code.

  • Auto Generated Code -- I felt that in order to be able to easily use Abigail-II's framework autogenerated code was essential. This was to my mind the only way to keep the various tasks synchronized. (Such as serialization and the deletion of attributes on DESTROY). This also appeals to my sense of lazyness as the approach is a touch verbose for my taste and this makes it easier to play with.
  • Subclassed Dumper -- In order to make it possible that the object can be dumped anytime any place without damage it is either require to subclass or patch Data::Dumper. I took the former approach. Amongst other problems was that using Freezer also has the unfortunate side effect of causing the object dumped to go into a "frozen" state after dumping. The sub class handles reversing this process.
  • Mess with UNIVERSAL -- As mentioned before the implementation of Freezer/Toaster in Data::Dumper is at least partially broken. It only accomdates one Freezer/Toaster method for all objects, and then it applies both indiscriminately to any blessed objects, whether they need them or not. This more or less entails that UNIVERSAL needs to be interfered with to prevent copious warning messages when dumping InsideOut and non InsideOut objects at the same time.
Anyway, heres the code, and an example script.

Class::Attributes::InsideOut --site/lib/Class/Attrbutes/InsideOut.pm

#Basic "InsideOut" object #from http://perlmonks.org/index.pl?node_id=178518 #package BaseballPlayer::Pitcher; #{ # use vars '@ISA'; # @ISA = 'BaseballPlayer'; # # my (%ERA, %Strikeouts); # # sub ERA : lvalue {$ERA {+shift}} # sub Strikeouts : lvalue {$Strikeouts {+shift}} # sub DESTROY { # my $self = shift; # delete $ERA {$self}, $Strikeouts {$self} # } #} package Class::Attributes::InsideOut; use Carp (); use Data::Dumper; use strict; no strict 'refs'; our $DEBUG; BEGIN { # see if we can get Scalar::Util to do our dirty work, # its faster than parsing overload::StrVal($ref) unless (eval" require Scalar::Util; *refaddr=*Scalar::Util::refaddr{CODE}; 1; ") { # Nope. Didn't seem to work. We dont have Scalar::Util avalabl +e. warn "Failed require Scalar::Util" if $DEBUG; #figure out where the ID is in a stringified bless reference. my $IDXOFS= -(length(bless {})-rindex(bless({}),"(")); require overload; # Note the $IDXOFS interpolates into a constant when we eval i +t. eval " sub refaddr { return ref \$_[0] ? substr(overload::StrVal(\$_[0]),$IDXOFS) : undef; }; 1; " or die $@; } } sub import { my $caller=caller; my $pack=shift; print Data::Dumper->Dump([$pack,\@_],[qw(pack *_)]) if $DEBUG; !@_ and Carp::confess("No arguments to Class::Attributes::Insideou +t"); my $isa=""; if (ref $_[0] ) { $isa=" ".join(" ",@{shift(@_)}); } if (my @badargs=grep{/\W/}@_) { Carp::confess("Illegal arguments @badargs"); } my @snippets=map{ "sub $_ : lvalue {\$$_\{" . "Class::Attributes::InsideOut::refaddr(shift)" . "||Carp::confess 'not a reference!'}}"; } @_; # Line matching /^\s*>/ are "Here_Doc" quoted strings. my @dump=map { (my $code=<<"_EOF_CODE")=~s/^\s*>/ /mg;$code; > \$as_hash{$_}=\$$_\{\$self} > if exists (\$$_\{\$self}); _EOF_CODE }@_; (my $snippet=<<"_EOF_CODE")=~s/^\s*>/ /mg; >{ > package $caller; > > use vars qw/\@ISA/; > \@ISA=qw($isa InsideOut::Class ); > > my (@{[join ", ",map{"%$_"}@_]}); > > @{[join "\n\t",@snippets]} > > sub __As_Hash__ { > my \$selfobj=shift; > my \$self=Class::Attributes::InsideOut::refaddr(\$self +obj); > print "$caller\::__As_Hash__(\$self)\\n" > if \$Class::Attributes::InsideOut::DEBUG; > my %as_hash; >@{[join(" ",@dump)]} > scalar(keys(%as_hash)) ? \\%as_hash : () > } > > sub __DESTROY__ { > my \$selfobj=shift; > my \$self=Class::Attributes::InsideOut::refaddr(\$self +obj); > print "$caller\::__DESTROY__(\$self)\\n" > if \$Class::Attributes::InsideOut::DEBUG; > @{[join ";\n ",map{"delete \$$_\{\$self}"}@_]}; > \$_->can("__DESTROY__") and > &{\$_."::__DESTROY__"}(\$selfobj) > foreach \$selfobj->_parents; > } >} >1; _EOF_CODE eval $snippet or die "Eval\n$snippet\nfailed with the error $@"; print $snippet if $DEBUG; } 1; package InsideOut::Class; sub new {bless {},shift} sub __Parents__ { my ($selfobj)=(@_); my $self=Class::Attributes::InsideOut::refaddr($selfobj); print ref($selfobj)."::__Parents__($self)\n" if $Class::Attributes::InsideOut::DEBUG; my %hash; my @queue=[ref $selfobj,0]; my @list; while (@queue) { my ($pack,$depth)=@{shift @queue}; next if defined $hash{$pack}; $hash{$pack}=$depth++; unshift @list,$pack; foreach my $item ( @{$pack."::ISA"} ) { push @queue,[$item,$depth]; } } @list } sub __Freezer__ { my ($selfobj)=(@_); my $self=Class::Attributes::InsideOut::refaddr($selfobj); print ref($selfobj)."::__Freezer__($self)\n" if $Class::Attributes::InsideOut::DEBUG; my @list=$selfobj->__Parents__; my $class=ref $selfobj; bless $selfobj,"Frozen::InsideOut::Class::Root"; my $return=bless { "-self" => $selfobj, "-class" => $class, ( map { if ($_->can('__As_Hash__')) { my $frozen=&{$_."::__As_Hash__"}($se +lfobj); $frozen ? ( $_ => $frozen ) : () } else { () } } @list ) },"Frozen::InsideOut::Class"; return $return; } sub DESTROY { my ($selfobj)=(@_); my $self=Class::Attributes::InsideOut::refaddr($selfobj); print ref($selfobj)."::DESTROY($self)\n" if $Class::Attributes::InsideOut::DEBUG; my @parents=reverse $selfobj->__Parents__; foreach (@parents) { $_->can("__DESTROY__") and &{$_."::__DESTROY__"}($selfobj); } } 1; package Frozen::InsideOut::Class; sub Toaster { my $obj=shift; print ref($obj)."::__Toaster__($obj)\n" if $Class::Attributes::InsideOut::DEBUG; foreach my $pack (keys %$obj) { next if $pack =~/\W/; foreach my $attr (keys %{$obj->{$pack}}) { &{"$pack\::$attr"}($obj->{-self})=$obj->{$pack}{$attr}; } } return bless $obj->{-self},$obj->{-class}; } 1; __END__ =head1 NAME Class::Attributes::InsideOut - Base class generator for inside-out classes which know how to serialize themselves. =head1 SYNOPSIS package Baz; use Class::Attributes::InsideOut qw(baz bop); package Bar; use Class::Attributes::InsideOut '@ISA'=>[qw(Foo Baz)],qw(foo bang); =head1 DESCRITION Evals into existance the required code for a class based on Abigails "inside-out" OO design pattern. The created modules can be (relatively) safely serialized with L< Data::Dumper::InsideOut | Data::Dumper::InsideOut >. In addition, accessors won't get confused if the class changes, although of course they may not get called, but if they do they are guaranteed to use the correct data. Cleanup on destroy is automatically facilitated. In order to do this all objects created from this class are subclassed + from InsideOut::Class (which is automatically used at the same time as + this module). =over 4 =item use Class::Attributes::InsideOut qw(foo bar baz); The interface is simple. Inside of the package you wish to create you use() this module with a list of attribute names. If the class is a subclass then it is necessary to provide the parent classes in a arrayref as the first parameter in the use clause. That or unshift them onto the packages @ISA after the use. The attributes are lvalues into independant lexically scoped hashes, keyed on the reference address. This class provides the means to obtain this transparently and consistantly via the subroutine =item Class::Attributes::InsideOut::refaddr() Which returns the reference address of the passed object. If possible this will just be a call into Scalar::Util::refaddr, otherwise it will + be obtained by the much slower parsing of the return of overload::StrVal($ref). This means that reblessing of the objects does not change the key used + to look them up for the various accessors. Such as when using class type to track object state. =item $Class::Attributes::InsideOut::DEBUG Setting C<$Class::Attributes::InsideOut::DEBUG=1> in a begin block before the use clause will cause the generated code to be printed to STDOUT. =back =head1 NOTE A number of special methods are created. In order to minimize the chance of these colliding with anything they are prefixed and postfixed by 2 underbars. Ie "__DESTROY__". It is important these methods dont get overriden. =head1 WARNING @ISA relationships are used to determine what values need to be serialized and destroyed. It may be necessary to improve the logic used to determine which hash values need to be deleted upon an objects destroy. Currently this should be done by overriding the base classes DESTROY method (don't forget to call SUPER::DESTROY however). Caching could be implemented for the DESTROY. Currently it will do a depth first traversal, deepest leftmost first through all the ancestors looking for a __DESTROY__ method. =head1 BUGS In code this funky almost certainly. YMMV. Patches welcome. =head1 AUTHOR and COPYRIGHT Module Copyright by demerphq - Yves Orton Dec 2002 Based on ideas and code snippet at http://perlmonks.org/index.pl?node_id=178518 by and copyright - Abigail 2002 Released under the Perl Artisitic License. =head1 SEE ALSO L<Perl> =cut

Data::Dumper::InsideOut -- site/lib/Data/Dumper/InsideOut.pm

package Data::Dumper::InsideOut; use Data::Dumper(); require Exporter; @ISA = qw(Exporter Data::Dumper); @EXPORT = qw(Dumper); use strict; sub Dumper { # don't want Data::Dumper objects, want the subclass. return Data::Dumper::InsideOut->Dump([@_]); } sub Dump { # The XS routine doesnt know about us goto &Data::Dumper::InsideOut::Dumpperl; } sub new { # we need to add some attributes to the dumper object my $s=shift; my $obj=$s->SUPER::new(@_); @{$obj}{qw(frigid frozen freezer _freezer toaster)} =({},{},"__Inside_Out_Freezer__","__Freezer__","Toaster") +; $obj; } sub Dumpperl { my $s=shift; $s = $s->new(@_) unless ref $s; my @out=$s->SUPER::Dumpperl(@_); foreach my $type (keys %{$s->{frigid}}) { # undo any blessing caused by freezing. foreach my $itm (@{$s->{frigid}{$type}}) { $itm=bless $itm,$type; } } wantarray ? @out : join('', @out); } sub _dump { my($s, $val, $name) = @_; my $type = ref $val; my $return; if ($type) { if ($s->{freezer} and UNIVERSAL::can($val,$s->{_freezer})) { my ($id)=overload::StrVal($val)=~/\((.*?)\)/; unless ($s->{frozen}{$id}++) { # store the class type of this guy so we can restore it la +ter. push @{$s->{frigid}{$type}},$val; my $freezer=$s->{freezer}; return $s->SUPER::_dump($val->$freezer(),$name); } else { # already stored return $s->SUPER::_dump($val,$name); } } elsif ($s->{toaster}) { # remove the Toaster on objects that cant my $return=$s->SUPER::_dump($val,$name); $return=~s/->$s->{toaster}\(\)$//; return $return } } return $s->SUPER::_dump($val,$name); } 1; package UNIVERSAL; sub __Inside_Out_Freezer__ { my $self=shift; # prevent non toaster objects from screaming. $self->can("__Freezer__") ? $self->__Freezer__ : $self } 1; __END__ =head1 NAME Data::Dumper::InsideOut - Data::Dumper subclass that knows how to serialize "Inside-Out" objects created using L<Class::Attributes::InsideOut|Class::Attributes::InsideOut> =head1 SYNOPSIS use Data::Dumper::InsideOut; print Dumper($inside_out_obj); =head1 DESCRITION See Data::Dumper. Ignore anything to do with Toaster or Freezer there and youll be fine. =head1 WARNING Using this module cause the method __Inside_Out_Freezer__ to be added to UNIVERSAL object. =head1 BUGS In code this funky almost certainly. YMMV. Patches welcome. =head1 AUTHOR and COPYRIGHT Copyright by demerphq - Yves Orton Dec 2002 Released under the Perl Artisitic License. =head1 SEE ALSO L<Perl>, L<Class::Attributes::InsideOut> =cut

A test script -- test_insideout.pl

#!perl -l BEGIN { $Class::Attributes::InsideOut::DEBUG=0; } package Foo; use Class::Attributes::InsideOut qw(foo bar baz); package Baz; use Class::Attributes::InsideOut qw(baz bop); package Bar; use Class::Attributes::InsideOut [qw(Foo Baz)],qw(foo bang); package Plain; sub new { bless [@_],shift } package main; use Data::Dumper::InsideOut; sub check { my $obj=shift; print "# Data::Dumper\n".Data::Dumper::Dumper($obj); my $dump=Dumper($obj); print "\n# Data::Dumper::InsideOut\n".$dump; my $new=eval $dump or die "$dump $@"; print "\n# Evaled Data::Dumper::InsideOut\n".Dumper($new); } my $obj=Foo->new(); $obj->foo=10; $obj->bar=[qw(a b c)]; $obj->baz="Inside-Out"; my $bar=Foo->new(); $bar->foo="foo"; bless $bar,"Baz"; $bar->baz="baz"; $bar->bop="bop"; bless $bar,"Bar"; $bar->bang="Bang!"; $bar->foo="bar"; $bar->bop="bar"; $bar->bar=$obj; check($obj); check($bar); check(Plain->new($bar)); __END__
And Finally, What a mixed inside-out, "normal" object looks like when dumped properly
# last dump from check(Plain->new($bar)); # Evaled Data::Dumper::InsideOut $VAR1 = bless( [ 'Plain', bless( { 'Foo' => { 'foo' => 'foo', 'bar' => bless( { 'Foo' => { 'foo' => 10, 'baz' => 'Inside-Out', 'bar' => [ 'a', 'b', 'c' ] }, '-self' => bless( {}, 'Frozen::InsideOut::Class::Ro +ot' ), '-class' => 'Foo' }, 'Frozen::InsideOut::Class' )->Toaster() }, '-self' => bless( {}, 'Frozen::InsideOut::Class::Root' ), 'Bar' => { 'foo' => 'bar', 'bang' => 'Bang!' }, 'Baz' => { 'baz' => 'baz', 'bop' => 'bar' }, '-class' => 'Bar' }, 'Frozen::InsideOut::Class' )->Toaster() ], 'Plain' );
As you can see the special "inside-out" attributes are dumped as though they existed in a seperate hash per package the attributes belong to. And when they are Toaster()ed the appropriate updates are made using the packages attribute accessors, (without using OO).

The amount of hassle needed to serialize these modules suggests to me that for a beginner inside out objects will probably be harder to use than more traditional approaches. They may be safer in general, but I think beginners depend on being able to see the data structures that they are working on and not being able to will make deeper comprehension difficult. Hopefully the framework above makes that job a bit easier.

Anyway thanks to whole host of people for motivating this node in one way or another.
jreades Abigail-II adrianh shotgunfx fruiture BrowserUk merlyn TheDamian GSAR TOMC
and a whole host of other players no doubt. /msg me if you think I have forgotten you.

Hope this was interesting, and please let me know about any suggestions or comments you might have. (I can think of bunch of areas ripe for improvement, but enough is enought for today :-)

updated:Minor typographical edits and readmore changes. And later again removed a superfluous duplicate line from the code.

--- demerphq
my friends call me, usually because I'm late....

Replies are listed 'Best First'.
Re: Yet Another Perl Object Model (Inside Out Objects)
by adrianh (Chancellor) on Dec 15, 2002 at 04:06 UTC

    ++ (when I get tomorrows votes anyway)

    Interesting approach. I've got a class that does something similar using a different approach --- so I'll probably annoy everybody with YAPOM class later on today :-)

    The thing that I would disagree with is autogenerating a set/get method for every attribute. It's rare that I have objects where this is true. Even when it is true, there is some checking/transformation of arguments that takes place - making auto-generation like this impractical. I want access to those hashes!

    A quick comment on:

    A common problem that is attempted to be solved is that of internal attribute clashes in inherited modules. I personally find this a little strange as I tend to not find this to be a noteworthy issue in my day to day use of objects in perl.

    Basically, I agree, but it's not the only advantage that the technique gives you - you also get compile time checking of attribute access, which is a good thing, along with the ability to inherit from non-hash based classes.

    There is also the fact that, while these sorts of bugs are not common, they can be complete sods to discover.

    I can't remember the precise details, but we had a bug that was showing up on some servers and not on others. It turned out that we had updated module A, which sometimes caused an update of module B (depending on the version currently installed), which was a subclass of module C, which was used by our class D.

    The latest version of B added an entry to the hash that was also used by D, breaking it in certain conditions.

    Took us two days to figure out what was going on.

    However - this sort of thing is rare :-)

      I want access to those hashes!

      Yeah, I thought about that. I figured that it more or less violated the flywieght pattern that Abigail was using, so I could justify keeping that for an update or later version. :-) Patches welcome.

      As for the bug that you guys encountered, one thing that can be helpful is to prevent modules from being updated by using file security. That way unexpected changes arent introduced into a running system. I agree with you that this type of error is unusual however.

      --- demerphq
      my friends call me, usually because I'm late....

        Yeah, I thought about that. I figured that it more or less violated the flywieght pattern that Abigail was using...

        I'm a little confused about why people keep referring to Abigail-II's object implementation as being based on the flyweight pattern.

        According to my copy of Design Patterns a flyweight pattern is:

        Using sharing to support large numbers of fine-grained objects efficiently

        The example they use in the book is character objects in a word processor. Obviously having a separate object for each input character is going to have a huge resource overhead. So, instead, you create a single pool of objects - one per character - and use the same object for every instance of "a", etc.

        Another example from my own experience in perl was when I worked on a project that had a large number of database tables that consisited of static data - lookup tables of info.

        Rather than spend a lot of time doing a SELECT every time I needed the info I slurped up the entire table, made a pool of static objects, and served up the appropriate one on request.

        To be useful flyweights can't have extrinsic state - no context dependant information - just intrinsic state.

        So I can't see any relation between flyweights and inside out objects. Does the term mean something different in the perl world? If so - what, and why would accessing the hashes directly violate it?

        As for the bug that you guys encountered, one thing that can be helpful is to prevent modules from being updated by using file security. That way unexpected changes arent introduced into a running system. I agree with you that this type of error is unusual however.

        Oh yes. I had a long, and somewhat painful, chat about the source control system after that incident :-)

        I agree that the version mixup shouldn't have happened, but it wouldn't have been a problem if we'd used inside out objects.

        (and I just want to say again that kind of problem is very rare in my experience)

Re: Yet Another Perl Object Model (Inside Out Objects)
by BrowserUk (Patriarch) on Dec 15, 2002 at 02:12 UTC

    A question: Why do you serialise objects?

    By this I mean you specifically, as opposed to why does anyone?

    Though the question is open to anyones specific use of serialisation of objects, so I guess the question I am really asking is under what circumstances have people used serialisation in the real world?

    Why did they do it and what did they get from it?


    Examine what is said, not who speaks.

      Why do I serialize objects? I suppose because to certain extent I am a visual person. During the development phase of writing a module I usually dump out data structres and print tables of information. If a data structure is complex then seeing the ouput from a dumper can really help to see whats going (wr)on(g).

      Also, for more serious uses, persistancy is a not uncommon requirement and serialization can be the simplest way to achieve it. Config files are a simple (and sometime abused) example but better to look at something like MLDBM.

      And in my case, writing and breaking and fixing a number of different serialization modules (aka dumpers) has been both a hobby and an heavy duty course on perl esoterica. Some people do regexes or crazy ties, me I do dumpers. I hope to have a super duper improved version of Data::BFDump ready after the holidays. It should make dumping inside out objects a _lot_ easier.

      :-)

      --- demerphq
      my friends call me, usually because I'm late....

        I probably should have waited for a few other to have responded to my question before replying, but it struck me that serialising objects from the outside is actually a rather strange requirement, and can only be done in perl because of the peculiar nature (relatively speaking) of the way OO is implemented in perl.

        In most OO languages, if serialisation is required, it is done by requesting the object to serialise itself and return that to the caller. Which is what I added to my version of the Quote class like this.

        sub toString{ my $self = shift; sprintf '[%s:' . '%s; 'x2 .'%s]', $self, $phrase{$self}, $author{$self}, $approved{$self}; }

        and to its super class QuotePlus like this:

        sub toString{ my $self = shift; sprintf '[%s:' . '%s;' x 1 . '%s]', $self, $date{$self}, $self->SUPER::toString(); }

        Of course, I haven't written the obverse fromString method yet, but I don't actually see the need for it. The toString method would be used mostly for debugging and perhaps for inclusion into error messages. I don't see me having a need to recreate instances of a class from their serialised form.

        I also added what I would see as a class method rather than an instance method, to dump all instance data for a class. For this I did use Data::Dumper like this.

        # QuotePlus (sub)class sub _dump{ warn "_dump should only be called as a class method.\nIe. QuotePlu +s::_dump()", and return if ref +shift; Data::Dumper->Dump( [ \%date ], [ 'date' ] ) . Quote::_dump(); } ... # Quote class ...sub _dump{ warn "_dump should only be called as a class method.\nIe. Quote::_ +dump()" and return if ref +shift; Data::Dumper->Dump( [ \%phrase, \%author, \%approved, ], [ qw/phrase author approved/ ] ); }

        Barring this from being called as an instance method makes sense to me as what is returned is class specific rather than instance specific.

        In terms of persistance, I'm torn between whether a class should know how to persist itself, of whether this should be done eternally, or by inheritance from a named class.

        It seems attractive at first to see this as a responsibility of the class itself, but then the persistance mechanism becomes fixed and requires modification of you change your database for example.

        I can see the merit in doing this from outside the class at the application level, in terms of "serialise & save" as this allows different applcations to use the same classes and different persistant storage.

        However, I see a problem with this in that if I have two subclasses of a base class and I want to persist all instances of one. Dumping one of the subclasses (via a class dump method) and saving it is going to also save instances of the base class that were created via the second subclass (if your using the Inside Out method).

        However, doing it one at a time through an instance method requires me to iterate over every instance I have created. Possibly safer, but painful none the less.

        The third method I see, is to have every class inherit (either directly or indirectly) from a Persistance class and it would provide each instance with a Persist method and a virtual class method PersistAll that a class could override to cause it to persist all of its instances. This allows the Persistance class to be swapped out for a new one whenever you change your storage medium, database etc.

        I note for the readers that I have done very little in way of OO in perl, and have never had to write this type of library classes in other OO languages, always having just made use of existing classes for these sort of things. I will say that I have always been disappointed in those I have used in Java, C++ and even my breif forays in SmallTalk many years ago.


        Examine what is said, not who speaks.

Re: Yet Another Perl Object Model (Inside Out Objects)
by diotalevi (Canon) on Dec 15, 2002 at 06:24 UTC

    I don't have my copy of _Design_Patterns_ handy but isn't this just a Flyweight implementation?

      Hi diotalevi, I responded to this in Re: Re: Re: Flyweights - different meaning in perl? as it seemed to fit into the general thread better, and I didnt want to split it up or duplicate myself.

      However the short answer is IMO: Nope.

      Sorry bout not replying in more detail here,

      --- demerphq
      my friends call me, usually because I'm late....

Re: Yet Another Perl Object Model (Inside Out Objects)
by Anonymous Monk on Sep 23, 2009 at 00:16 UTC
    I was watching a tech talk by Doug Crockford called JavaScript: The Good Parts and JavaScript has inside-out objects, who knew :)
    var singleton = (function () { var privateVariable; function privateFunction(x) { ...privateVariable... } return { firstMethod: function (a, b) { ...privateVariable... }, secondMethod: function (c) { ...privateFunction()... } }; }());

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlmeditation [id://219924]
Approved by Zaxo
Front-paged by diotalevi
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others cooling their heels in the Monastery: (5)
As of 2024-03-19 08:40 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found