Yuki Kimoto recently posted about the latest release of Object::Simple, billed as “the simplest class builder”. Since I’ve also written a “simple” OO framework called Class::Tiny, I thought I’d point out similarities and differences.
(I’m not going to address Object::Simple’s origins from or differences from Mojo::Base.)
Similarities 🔗︎
Single file, minimal dependencies
Both Object::Simple and Class::Tiny are single-file OO frameworks with no no-core dependencies on recent perls. According to the “sloccount” tool, Object::Simple is 98 lines. Class::Tiny is 135.
Class::Tiny does require some dependencies on older Perls for deep @ISA introspection and global destruction detection.
Accessor generation with lazy defaults
Both frameworks allow you to specify accessors and provide either scalar or code-reference defaults for them. Defaults are evaluated on first use. The underlying generated code is extraordinarily similar and accessor speeds are generally comparable (at least with Class-Tiny-1.05 which has some optimizations to remove scopes).
Read-write accessors
Both offer read-write accessors, which I think is the only sensible choice when providing only a single style.
Differences 🔗︎
Mutator return style
Class::Tiny mutators return the value just set, which is consistent with the values returned by accessors. Object::Simple mutators return the invocant, which allows chaining.
BUILD/BUILDARGS/DEMOLISH
Class::Tiny supports the BUILD/BUILDARGS/DEMOLISH methods just like Moose and Moo do. Object::Simple does not.
Notably, Class::Tiny supports an interoperability convention that allows Moo or Moose classes to inherit from a Class::Tiny class without calling BUILD methods more than once.
Constructor speed
Because Class::Tiny does some extra validation, plus provides BUILD/BUILDARGS support, its constructor is about 3x slower than Object::Simple, which has a two-line constructor.
Extraneous methods in @ISA
Class::Tiny classes inherit from Class::Tiny::Object, which provides only new
, BUILDALL
and DESTROY
methods. Object::Simple classes typically inherit from Object::Simple, which provides import
, new
, attr
, class_attr
and dual_attr
methods.
Unknown constructor arguments
Class::Tiny ignores unknown attributes in constructor arguments (without error, just like Moose/Moo). Object::Simple will include them in the constructed object.
Subclassing
Class::Tiny relies on users to set inheritance with @ISA or base/super/parent pragmas. Object::Simple additionally offers an import flag “-base” which sets the superclass. If the superclass is not Object::Simple, the superclass is loaded.
Introspection
Class::Tiny provides a mechanism for getting a list of class attributes and default values for attributes. Object::Simple does not.
strict/warnings export
Object::Simple turns on strict and warnings in the caller when the “-base” flag is used. Class::Tiny does not.
Closing thoughts 🔗︎
Object::Simple is, indeed, simple. It’s not much more than syntactic sugar for generating accessors with defaults.
That said, I think it’s too simple. If you really need minimal overhead and maximum speed just bless a hash reference into a class and directly access the members. If you want minimalism and default values, you can get there with eager defaults like this:
sub new { my $class = shift; return bless { name => "Jane", data => {}, @_ }, $class; }
Once you start subclassing, I think you’ll want BUILD/DEMOLISH support to properly order construction and teardown and Object::Simple doesn’t give it to you.
Even if you don’t plan to subclass, might that be something your downstream users might want to do? Providing BUILD/DEMOLISH support makes it easy for downstream users to have well-structured construction and teardown.
Yes, you can create custom constructors, but that defeats the syntactic simplicity of Object::Simple. Plus, if you have custom constructors, you’ll need custom destructors and a mechanism for ensuring they get called in order. Very soon, you’ll have re-invented the semantics of BUILD/DEMOLISH. So why not start with a framework that already provides that for you?
I think Object::Simple fits a very narrow use-case: people who want lazy defaults, don’t want to subclass and are willing to add a dependency to avoid some typing.
For general use, I still think Moo is the best all-around choice unless you know for sure that you need the introspection and meta-class hackery that Moose offers.
If Moo is too “heavy” for me for some project, I’ll use Class::Tiny. If Class::Tiny is too “heavy” (?!?), then I’ll just roll my own class and avoid the dependency entirely.
Admittedly, I’m biased, but I can’t think of a situation where I’d actually use Object::Simple as it stands.
If Object::Simple added BUILD/DEMOLISH support, then it might be a decent alternative – a different flavor of simple class builder for those who like its particular API choices (e.g. mutator chaining). Until then, I think it’s too niche to put in my toolbox.