2 package Class::MOP::SafeMixin;
7 use Scalar::Util 'blessed';
10 our $VERSION = '0.01';
12 use base 'Class::MOP::Class';
15 # fetch the metaclass for the
16 # caller and the mixin arg
17 my $metaclass = shift;
18 my $mixin = (shift)->meta;
20 # according to Scala, the
21 # the superclass of our class
22 # must be a subclass of the
23 # superclass of the mixin (see above)
24 my ($super_meta) = $metaclass->superclasses();
25 my ($super_mixin) = $mixin->superclasses();
26 ($super_meta->isa($super_mixin))
27 || confess "The superclass must extend a subclass of the superclass of the mixin"
28 if defined $super_mixin && defined $super_meta;
30 # collect all the attributes
31 # and clone them so they can
32 # associate with the new class
33 my @attributes = map {
34 $mixin->get_attribute($_)->clone()
35 } $mixin->get_attribute_list;
38 my $method = $mixin->get_method($_);
39 # we want to ignore accessors since
40 # they will be created with the attrs
41 (blessed($method) && $method->isa('Class::MOP::Attribute::Accessor'))
42 ? () : ($_ => $method)
43 } $mixin->get_method_list;
46 # I assume that locally defined methods
47 # and attributes get precedence over those
50 # add all the attributes in ....
51 foreach my $attr (@attributes) {
52 $metaclass->add_attribute($attr)
53 unless $metaclass->has_attribute($attr->name);
56 # add all the methods in ....
57 foreach my $method_name (keys %methods) {
58 $metaclass->alias_method($method_name => $methods{$method_name})
59 unless $metaclass->has_method($method_name);
71 Class::MOP::SafeMixin - A meta-object for safe mixin-style composition
77 This is a meta-object which provides B<safe> mixin-style composition
78 of classes. The key word here is "safe" because we enforce a number
79 of rules about mixing in which prevent some of the instability
80 inherent in other mixin systems. However, it should be noted that we
81 still allow you enough rope with which to shoot yourself in the foot
88 In order to mix classes together, they must inherit from a common
89 superclass. This assures at least some level of similarity between
90 the classes being mixed together, which should result in a more
93 The only exception to this rule is if the class being mixed in has
94 no superclasses at all. In this case we assume the mixin is valid.
98 Since we enforce a common ancestral relationship, we need to be
99 mindful of method and attribute conflicts. The common ancestor
100 increases the potential of method conflicts because it is common
101 for subclasses to override their parents methods. However, it is
102 less common for attributes to be overriden. The way these are
103 resolved is to use a Trait/Role-style conflict mechanism.
105 If two classes are mixed together, any method or attribute conflicts
106 will result in a failure of the mixin and a fatal exception. It is
107 not possible to resolve a method or attribute conflict dynamically.
108 This is because to do so would open the possibility of breaking
109 classes in very subtle and dangerous ways, particularly in the area
110 of method interdependencies. The amount of implementation knowledge
111 which would need to be known by the mixee would (IMO) increase the
112 complexity of the feature exponentially for each class mixed in.
114 However fear not, there is a solution (see below) ...
118 Safe mixin's offer the possibility of CLOS style I<before>, I<after>
119 and I<around> methods with which method conflicts can be resolved.
121 A method, which would normally conflict, but which is labeled with
122 either a I<before>, I<after> or I<around> attribute, will instead be
123 combined with the original method in the way implied by the attribute.
125 The result of this is a generalized event-handling system for classes.
126 Which can be used to create things more specialized, such as plugins
131 =head2 What kinda crack are you on ?!?!?!?
133 This approach may seem crazy, but I am fairly confident that it will
134 work, and that it will not tie your hands unnessecarily. All these
135 features have been used with certain degrees of success in the object
136 systems of other languages, but none (IMO) provided a complete
139 In CLOS, I<before>, I<after> and I<around> methods provide a high
140 degree of flexibility for adding behavior to methods, but do not address
141 any concerns regarding classes since in CLOS, classes and methods are
142 separate components of the system.
144 In Scala, mixins are restricted by their ancestral relationships, which
145 results in a need to have seperate "traits" to get around this restriction.
146 In addition, Scala does not seem to have any means of method conflict
147 resolution for mixins (at least not that I can find).
149 In Perl 6, the role system forces manual disambiguation which (as
150 mentioned above) can cause issues with method interdependecies when
151 composing roles together. This problem will grow exponentially in one
152 direction with each role composed and in the other direction with the
153 number of roles that role itself is composed of. The result is that the
154 complexity of the system becomes unmanagable for all but very simple or
155 very shallow roles. Now, this is not to say that roles are unusable, in
156 fact, this feature (IMO) promotes good useage of roles by keeping them
157 both small and simple. But, the same behaviors cannot be applied to
158 class mixins without hitting these barriers all too quickly.
160 The same too can be said of the original Traits system, with its
161 features for aliasing and exclusion of methods.
163 So after close study of these systems, and in some cases actually
164 implementing said systems, I have come to the see that each on it's
165 own is not robust enough and that combining the best parts of each
166 gives us (what I hope is) a better, safer and saner system.
170 Stevan Little E<lt>stevan@iinteractive.comE<gt>
172 =head1 COPYRIGHT AND LICENSE
174 Copyright 2006 by Infinity Interactive, Inc.
176 L<http://www.iinteractive.com>
178 This library is free software; you can redistribute it and/or modify
179 it under the same terms as Perl itself.