Comparison of Class::Tiny and Object::Simple

Reading time: 4 minutes

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.

•      â€¢      â€¢

If you enjoyed this or have feedback, please let me know by or