Multi-Module Alternative to the Factory Pattern?

I've been using a factory pattern for a set of packaging classes
--classes that generate different package formats (eg. Debian, RPM,
etc.), but it's not quite the right fit b/c it leads to repetition of
the staging step. Yuk! So I think a better fit is to have one class
(Packager) that calls on external modules (or classes) for each
different format. But, unfortuately there are quite a few attributes
needed by each from the Packager, and it's just doesn't seem right to
pass that many variables. So it seems like it would be better to just
include the needed module into the Packager class as needed. But here
again I can't un-include a format module once done with it in order to
use another format. So I'm right back to using a factory pattern.

Then I had this idea. and I'm wondering what others think of it.

First we need this little extension from Facets:

  class Object

    # Like super but skips to a specific ancestor module or class.

···

#
    # class A
    # def x ; 1 ; end
    # end
    #
    # class B < A
    # def x ; 2 ; end
    # end
    #
    # class C < B
    # def x ; superior(A) ; end
    # end
    #
    # C.new.x #=> 1
    #
    def superior(klass=self.class.superclass, *args, &blk)
      unless self.class.ancestors.include?(klass)
        raise ArgumentError
      end
      called = /\`([^\']+)\'/.match(caller(1).first)[1].to_sym
      klass.instance_method(called).bind(self).call(*args,&blk)
    end

  end

Then:

  module Debian
    def pack ; "Debian package"; end
  end

  module RPM
    def pack ; "RPM package"; end
  end

  class Packager
    include Debian, RPM

    def pack( type )
      superior(type)
    end
end

  pkgr = Packager.new
  pkgr.pack(Debian) #=> "Debian package"
  pkgr.pack(RPM) #=> "RPM package"

What do you think?

Thanks,
T.

Trans schrieb:

I've been using a factory pattern for a set of packaging classes
--classes that generate different package formats (eg. Debian, RPM,
etc.), but it's not quite the right fit b/c it leads to repetition of
the staging step. Yuk! So I think a better fit is to have one class
(Packager) that calls on external modules (or classes) for each
different format. But, unfortuately there are quite a few attributes
needed by each from the Packager, and it's just doesn't seem right to
pass that many variables. (...)

Tom, couldn't you pass the packager itself to the external modules and let them query the attributes they need?

Regards,
Pit

Hi Pit--

Pit Capitain wrote:

Tom, couldn't you pass the packager itself to the external modules and
let them query the attributes they need?

Yes. That would work too. And that might be the best way to do it.
Though it still means writing code to do the attribute transfer, but
that can be automated fairly easily.

I'm still curious about this "multi-module" idea in general though.
Seems like it could be an alternative to code injection too.

T.

why not:

     harp:~ > cat a.rb
     module DEB
       A, B, C = %w( D E B )
       def self.pack_step_one() p A end
       def self.pack_step_two() p B end
       def self.pack_step_three() p C end
       def self.pack_step_four(arg) p arg end
     end

     module RPM
       A, B, C = %w( R P M )
       def self.pack_step_one() p A end
       def self.pack_step_two() p B end
       def self.pack_step_three() p C end
       def self.pack_step_four(arg) p arg end
     end

     class Packager
       def initialize type
         @type = type
       end
       def pack arg
         @type.instance_eval{
           pack_step_one
           pack_step_two
           pack_step_three
           pack_step_four arg
         }
       end
     end

     packager = Packager.new DEB
     packager.pack 42

     harp:~ > ruby a.rb
     "D"
     "E"
     "B"
     42

??

btw. for these kinds of setups: modules with state (singletons) i've been
using prototypes lately:

     harp:~ > cat a.rb
     require 'prototype'

     DEB = prototype{
       @a, @b, @c = %w( D E B )
       def pack_step_one() p @a end
       def pack_step_two() p @b end
       def pack_step_three() p @c end
       def pack_step_four(arg) p arg end
     }

     RPM = prototype{
       @a, @b, @c = %w( D E B )
       def pack_step_one() p @a end
       def pack_step_two() p @b end
       def pack_step_three() p @c end
       def pack_step_four(arg) p arg end
     }

     class Packager
       attr 'type'
       def initialize type
         @type = type
       end
       def pack arg
         @type.instance_eval{
           pack_step_one
           pack_step_two
           pack_step_three
           pack_step_four arg
         }
       end
     end

     packager = Packager.new DEB
     packager.pack 42

     harp:~ > ruby -r rubygems a.rb
     "D"
     "E"
     "B"
     42

i guess i'm failing to see what multi-module inclusion givs you that delegation
plus argument passing (into the delegate module not the other way around)
doesn't?

regards.

-a

···

On Thu, 28 Sep 2006, Trans wrote:

Hi Pit--

Pit Capitain wrote:

Tom, couldn't you pass the packager itself to the external modules and
let them query the attributes they need?

Yes. That would work too. And that might be the best way to do it.
Though it still means writing code to do the attribute transfer, but
that can be automated fairly easily.

I'm still curious about this "multi-module" idea in general though.
Seems like it could be an alternative to code injection too.

T.

--
in order to be effective truth must penetrate like an arrow - and that is
likely to hurt. -- wei wu wei

why not:
[snip]

Only becuase there are a lot of arguments to pass.

btw. for these kinds of setups: modules with state (singletons) i've been
using prototypes lately:

Right on. I do like the prototype stuff. Just don't have the time to
invest init at them moment though.

[snip cool example use of prototype]

i guess i'm failing to see what multi-module inclusion givs you that delegation
plus argument passing (into the delegate module not the other way around)
doesn't?

The advantage is access to the internal state --no passing of
attributes required. Esspecailly true when you have alot of
attributes/arguments involved. Simple example:

     require 'facet/kernel/as'

     module DEB
       A, B, C = %w( D E B )
       def pack_step_one() p A+a end
       def pack_step_two() p B+b end
       def pack_step_three() p C+c end
     end

     module RPM
       A, B, C = %w( R P M )
       def pack_step_one() p A+a+b+c end
       def pack_step_two() p B+b+c+a end
       def pack_step_three() p C+c+a+b end
     end

     class Packager
       include DEB, RPM
       attr :a,:b,:c
       def initialize
         @a,@b,@c = %w{ 1 2 3 }
       end
       def pack type
         as(type).pack_step_one
         as(type).pack_step_two
         as(type).pack_step_three
       end
     end

     packager = Packager.new
     packager.pack DEB, 42

     harp:~ > ruby a.rb
     "D123"
     "E231"
     "B312"
     42

Note #as works like #superior from my last example but uses magic-dot
notation to allow any method to be called. (It would be nice if it took
a block but I'll have to work on that).

T.

···

ara.t.howard@noaa.gov wrote:

Only becuase there are a lot of arguments to pass.

hash? array?

i guess i'm failing to see what multi-module inclusion givs you that
delegation plus argument passing (into the delegate module not the other
way around) doesn't?

The advantage is access to the internal state --no passing of attributes
required. Esspecailly true when you have alot of attributes/arguments
involved. Simple example:

    require 'facet/kernel/as'

    module DEB
      A, B, C = %w( D E B )
      def pack_step_one() p A+a end
      def pack_step_two() p B+b end
      def pack_step_three() p C+c end
    end

    module RPM
      A, B, C = %w( R P M )
      def pack_step_one() p A+a+b+c end
      def pack_step_two() p B+b+c+a end
      def pack_step_three() p C+c+a+b end
    end

    class Packager
      include DEB, RPM
      attr :a,:b,:c
      def initialize
        @a,@b,@c = %w{ 1 2 3 }
      end
      def pack type
        as(type).pack_step_one
        as(type).pack_step_two
        as(type).pack_step_three
      end
    end

    packager = Packager.new
    packager.pack DEB, 42

    harp:~ > ruby a.rb
    "D123"
    "E231"
    "B312"
    42

Note #as works like #superior from my last example but uses magic-dot
notation to allow any method to be called. (It would be nice if it took a
block but I'll have to work on that).

hmm. how bout this:

     harp:~ > cat a.rb
     class Object
       def including m, &b
         (class << (d=dup); self; end).module_eval{ include m }
         d.instance_eval &b
       end
     end

     module DEB
       A, B, C = %w( D E B )
       def pack_step_one() p A+a end
       def pack_step_two() p B+b end
       def pack_step_three() p C+c end
     end

     module RPM
       A, B, C = %w( R P M )
       def pack_step_one() p A+a+b+c end
       def pack_step_two() p B+b+c+a end
       def pack_step_three() p C+c+a+b end
     end

     class Packager
       [:a,:b,:c].each{|a| attr a}

       def initialize
         @a,@b,@c = %w{ 1 2 3 }
       end

       def pack type, *a
         including(type){
           pack_step_one
           pack_step_two
           pack_step_three
         }
       end
     end

     packager = Packager.new
     packager.pack DEB, 42

     harp:~ > ruby a.rb
     "D1"
     "E2"
     "B3"

sure the performance will suck though ;-(

fun stuff...

i like your 'as' method.

-a

···

On Fri, 29 Sep 2006, Trans wrote:
--
in order to be effective truth must penetrate like an arrow - and that is
likely to hurt. -- wei wu wei