1NAME
2 Class::MixinFactory::ReadMe - About the Mixin Class Factory
3
4SYNOPSIS
5 package MyClass;
6 use Class::MixinFactory -hasafactory;
7 sub new { ... }
8 sub foo { return "Foo Bar" }
9
10 package MyClass::Logging;
11 sub foo { warn "Calling foo"; (shift)->NEXT('foo', @_) }
12
13 package MyClass::UpperCase;
14 sub foo { uc( (shift)->NEXT('foo', @_) ) }
15
16 package main;
17
18 my $class = MyClass->class( 'Logging', 'UpperCase' );
19
20 print $class->new()->foo();
21 # Calls MyClass::Logging::foo, MyClass::UpperCase::foo, MyClass::foo
22
23ABSTRACT
24 This distribution facilitates the run-time generation of classes which
25 inherit from a base class and some optional selection of mixin classes.
26
27 A factory is provided to generate the mixed classes with multiple
28 inheritance. A NEXT method allows method redispatch up the inheritance
29 chain.
30
31MOTIVATION
32 The Challenge
33
34 When developing an object class that will be used by different people
35 for different purposes, I find myself drawn to solutions in which a
36 minimal base class provides the shared behavior they all need, and a
37 collection of subclasses provides layers of additional functionality.
38
39 For example, consider a text templating framework, which might be
40 separated into several elements:
41
42 * a base class, which provides methods to convert marked-up text into
43 runnable code,
44
45 * an extension which enhances security by runing the code in a Safe
46 compartment,
47
48 * an extension which feeds output through an HTML-escaping filter, and
49
50 * an extension which records internal profiling data for benchmarking
51 purposes.
52
53 (See Text::MicroMason for an example of this design.)
54
55 A Bad Approach
56
57 A naive implementation of this might use a subclass for each behaviour,
58 and look like the following:
59
60 +---------+
61 | Base |
62 +---------+
63 |
64 +-----------------+-----------------+
65 v v v
66 +---------+ +---------+ +---------+
67 |Benchmark| | Filter | | Safe | @ISA=qw(Base)
68 +---------+ +---------+ +---------+
69
70 The well-known problem with this implementation appears when you want to
71 combine several features:
72
73 +---------+
74 | Base |
75 +---------+
76 |
77 +-----------------+-----------------+
78 v v v
79 +---------+ +---------+ +---------+
80 |Benchmark| | Filter | | Safe | @ISA=qw(Base)
81 +---------+ +---------+ +---------+
82 | |
83 +--------+--------+
84 v
85 +-------------+
86 | Safe_Filter | @ISA=qw(Filter Safe)
87 +-------------+
88
89 This is the dreaded "diamond inheritance" problem: if Base provides a
90 compile() method, which Filter and Safe each override to perform
91 additional actions before or after calling SUPER::compile(), how can we
92 ensure they are all called in the correct sequence?
93
94 A Good Approach
95
96 The standard software engineering solution is to replace the use of
97 inheritance with decomposition into several different classes of
98 objects, which then cooperate through decoration and delegation; for
99 example, using separate classes for a resolver, a lexer, a parser, a
100 compiler, and an output channel. (See HTML::Mason for an example of this
101 design.)
102
103 Indeed, composition is an underutilized design technique, and there are
104 many times when inheritance is not the best tool to use. But of course,
105 in Perl there's more than one way to solve this problem, one of which is
106 facilitated by this distribution.
107
108 A Different Approach
109
110 We can rearrange our class hierarchy to avoid diamond inheritance by
111 using a base and a collection of mixin classes, which don't directly
112 inherit from the base class:
113
114 +---------+ +---------+ +---------+ +---------+
115 |Benchmark| | Filter | | Safe | | Base |
116 +---------+ +---------+ +---------+ +---------+
117 | | |
118 +-----------------+-----------------+
119 v
120 +-------------+
121 | Safe_Filter | @ISA=qw(Filter
122 +-------------+ Safe Base)
123
124 However, in this condition our mixin classes can't call SUPER methods at
125 all! Instead, another redispatch mechanism is needed, one that is able
126 to back-track through the inheritance tree and explore other branches.
127 (See the NEXT manpage for such an implementation.)
128
129 The order in which mixins are stacked is significant, so the caller does
130 need to have some understanding of how their behaviors interact. For
131 example, you'd typically want to ensure that the Benchmarking mixin was
132 the first in the chain, so that it could time everything later in the
133 sequence.
134
135 This Distribution
136
137 The Class::MixinFactory distribution provides serveral elements to
138 facilitate tihs kind of dynamic mixin architecture. The top level
139 package is just a facade that loads the other necessary classes and
140 provides a few import options for compile-time convenience. (See the
141 Class::MixinFactory manpage.)
142
143 To generate an object with some combination of mixins, you first use a
144 mixin factory to generate a mixed class. If a class with that
145 combination of classes has already been created, it is reused. You can
146 add a factory method to your base class, create a separate factory
147 object, or inherit to produce a factory class. (See the
148 Class::MixinFactory::Factory manpage.)
149
150 To allow mixin classes to redispatch to subsequent classes, all mixed
151 classes also inherit from a class which provides a NEXT() method. (If
152 you would prefer, your mixin class can alternately use the AUTOLOAD
153 solution provided by the NEXT::ACTUAL module from CPAN, or any other
154 equivalent re-dispatch mechanism.) (See the Class::MixinFactory::NEXT
155 manpage.)
156
157RELATED MODULES
158 There are number of other modules on CPAN that also support mixins,
159 method importing, or run-time multiple inheritance, while others don't
160 use mixins but are addressing a similar area of concern.
161
162 * The mixin, Class::Mixin, and Spiffy modules support mixin classes
163 but don't have a configurable factory object or support run-time
164 mixin selection.
165
166 * The Class::Mix and Class::Mutator modules provide run-time class
167 generation with multiple inheritance, but don't provide a
168 configurable factory object or a redispatch technique.
169
170 * The Class::Factory module has a factory interface, but doesn't
171 support multiple inheritance.
172
173 * The NEXT module provides a backtracking equivalent to SUPER similar
174 to the NEXT method included here, but uses AUTOLOAD rather than a
175 universal method.
176
177 * The Class::Delegate and other modules support decoration to address
178 this problem via decomposition.
179
180 * The Class::Role, Class::Roles and Class::Trait modules support
181 composing shared behaviors into your class.
182
183VERSION
184 This is version 0.92.
185
186 Elements of the interface remain open to change.
187
188BUGS
189 This module is new and relatively untested.
190
191 Please report any problems you encounter to the author at the below
192 address.
193
194INSTALLATION
195 This module should work with any version of Perl 5, without platform
196 dependencies or additional modules beyond the core distribution.
197
198 You should be able to install this module using the CPAN shell
199 interface:
200
201 perl -MCPAN -e 'install Class::MixinFactory'
202
203 Alternately, you may retrieve this package from CPAN
204 ("http://search.cpan.org/~evo/") or from the author's site
205 ("http://www.evoscript.org/Class-MixinFactory").
206
207 After downloading the distribution, follow the normal procedure to
208 unpack and install it, using the commands shown below or their local
209 equivalents on your system:
210
211 tar xzf Class-MixinFactory-*.tar.gz
212 cd Class-MixinFactory-*
213 perl Makefile.PL
214 make test && sudo make install
215
216SUPPORT
217 If you have questions or feedback about this module, please feel free to
218 contact the author at the below address. Although there is no formal
219 support program, I do attempt to answer email promptly.
220
221 I would be particularly interested in any suggestions towards improving
222 the documentation, correcting any Perl-version or platform dependencies,
223 as well as general feedback and suggested additions.
224
225 Bug reports that contain a failing test case are greatly appreciated,
226 and suggested patches will be promptly considered for inclusion in
227 future releases.
228
229 To report bugs via the CPAN web tracking system, go to
230 "http://rt.cpan.org/NoAuth/Bugs.html?Dist=Class-MixinFactory" or send
231 mail to "Dist=Class-MixinFactory#rt.cpan.org", replacing "#" with "@".
232
233 If you've found this module useful or have feedback about your
234 experience with it, consider sharing your opinion with other Perl users
235 by posting your comment to CPAN's ratings system
236 ("http://cpanratings.perl.org/rate/?distribution=Class-MixinFactory").
237
238 For more general discussion, you may wish to post a message on PerlMonks
239 ("http://perlmonks.org/?node=Seekers%20of%20Perl%20Wisdom") or on the
240 comp.lang.perl.misc newsgroup
241 ("http://groups.google.com/groups?group=comp.lang.perl.misc").
242
243AUTHOR
244 Developed by Matthew Simon Cavalletto at Evolution Softworks. You may
245 contact the author directly at "evo#cpan.org" or
246 "simonm#cavalletto.org", replacing "#" with "@".
247
248 Custom development and technical consulting are available at
249 "www.evolutionsoftworks.com". More free Perl software is available at
250 "www.evoscript.org".
251
252THANKS
253 My sincere thanks to the Perl Monks community for their feedback on
254 earlier versions of this commentary.
255
256 http://perlmonks.org/index.pl?node_id=398061
257 http://perlmonks.org/index.pl?node_id=399040
258
259LICENSE
260 Copyright 2004 Matthew Simon Cavalletto.
261
262 You may use, modify, and distribute this software under the same terms
263 as Perl.
264
265