« The Parnas Extraction | Main | Subtree Selection with Flatten/Select »

January 31, 2013

Comments

Hwstrbk

Indeed! Being required to extend a framework-classes for a basic use-case is a showstopper for me.

I am also not fond of AOP-like magic going on in the background, but I suppose there are some measures of complexity that are very hard to avoid...

Rasmus Schultz

I agree, this is unfortunately way too common, particularly in PHP frameworks.

In defense of PHP framework developers, however, they have good reasons to use superclasses - options for composition were traditionally very limited in the PHP language as such, and there was really no good way to provide convenience and consistency without resorting to a superclass to "fix" the key problems hindering good (or best) practices in PHP as such.

Hopefully the addition of traits will mitigate some of that. Though I'm sure it'll be a while before PHP traits are widely available and framework developers are willing to accept the dependency on a newer version of the language...

Danveloper

You've got to use the right tool for the job, and sometimes that's going to mean extending framework classes that are designed to give relief to your development effort.

It's nice when frameworks design around keeping the framework code isolated from the application code, but it shouldn't be the onus of the framework developers to do so, imo. It's up to us, as astute developers, to construct our application code and logic in a way such that we can integrate and disintegrate framework dependencies as is appropriate. To do otherwise demonstrates a lack of foresight.

Rob

Thank you for saying this out loud! Frameworks based on class extension are extremely appealing at first, particularly to the control freaks who want tight coupling in their applications (unbelievably I work with quite a few of those). Over time the loss of design flexibility and moving control from the "application" or the "problem domain" over to the framework leads to a lot of stale code that is hard to test and extend. In particular, I've seen systems with useful business logic that can't be reused because it's locked into framework hierarchies.

This is one of those anti-patterns that once you see it, it's hard to unsee it.

Marc Hamann

And here I thought frameworks were an anti-pattern in themselves...

F_lombardo

And what about annotations coupling? I think it's so underestimated!!!

Evi1M4chine

I always said that frameworks go against the very principles of good programming. They aren’t modules you can fit into your program. They want you to make a module to fit into their program. They don’t play nice with other frameworks or even libraries. You can’t just take a part of it and use what you need. They are monolithic, and hence really wrong.

But I also think this about the concept of “applications” (think e.g. browsers) as opposed to toolboxes you can compose at will (think bash programming and GNU tools).

And frameworks also tend to naturally (d)evolve into a inner-platform anti-pattern.

Un_Jon

Coupling is underestimated !! In haskell, scala (maybe in clojure with protocols), you can use a frameworks without having to extends it with the help of typeclasses.

For example, you can simply use JodaTime dates in Play! framework 2 with forms without play knowing of JodaTime ou JodaTime being depend of Play!.

David Allen

Was there a particular real-world framework you used that inspired you to write this post?

Philip_schwarz

I came across similar thoughts in the following two passages from "Holub on Patterns" (2004) http://www.amazon.co.uk/Holub-Patterns-Learning-Design-Looking/dp/159059388X/ref=sr_1_1 :

#1 Template Method has little to recommend it in most situations. Strategy for example, typically provides a better alternative.

Well done class libraries work "out of the box". You should be able to instantiate a framework class, and it should do something useful. Generally, the 90/10 rule applies (10 % of the functionality is used 90% of the time, so the 10 % should define the default behaviour of the class.) In template method, however, the framework often defines NO default behaviour, but rather you are required to provide subclasses to do anything useful. Given the 90/10 rule, this means you have to do unnecessary work 90% of the time. Template method does not prohibit the class designer from providing useful default functionality at the superclass level, expecting that the programmer will modify the behaviour of the superclass through derived-class overrides if necessary. In an OO system, though, using derivation to modify superclass behaviour is just run-of-the-mill programming that's hardly worth glorifying an an official pattern.

#2 Template method is best used in moderation. An entire class "framework" that depends on derivation-based customization is brittle in the extreme. The base classes are just too fragile. When I was programming in MFC, I had to rewrite all my applications every time M$ released a new version. Often the code compiled just fine but didn't work anymore because some base-class method had changed. Template method is not used in any of the code I supply in this book(Holub on Patterns).
It is a telling condemnation of Template Method that most of the Java-library code works pretty well out-of-the-box and that the Java libraries are more useful than MFC ever was. You can extend the Java classes if you need to customize them for an off-the-wall application, but they work just fine without modification for the vast majority of applications. This sort of out-of-the-box structure is just better than a derivation-based framework. It's easier to maintain, it's easier to use, and it doesn't put your code at risk if the vendor-supplied class changes its implementation.

Stefan Kanev

I think this makes a lot of sense in theory, but are there any practical examples? I am very curious to see a (say) web framework that works does that. I'm also very curious about the level of separation. For example, it makes sense not to inherit ActiveRecord::Base, but what else can be decoupled in a Rails app?

SharpsawDotOrg

I, like Mr. Kanev, would like to hear some concrete examples.

Actually, though I can picture how it would look to delegate-instead-of-inherit ActiveRecord::Base, I don't see how "Eventing" and "Listeners" would work.

Are the eventing/listeners options relevant to an alternative to ActiveRecord?

Thanks!

Thomas

My preferred framework is Django where the preferred method to implement applications is class inheritance.

To create models (or DAOs as Java gurus may say), one extends `django.db.models.Model`
To create views, one extends `django.views.generic.[View|DetailView|ListView...]`
To create administrative interfaces one extends `django.contrib.admin.ModelAdmin`.

The reason this works so well for people is because each of these base classes is meticulously thought out and brimming with functionality. Functionality that often times only needs a slight tweak in terms of an overloaded method or property. Without being able to use these base classes, there would be an explosion in boilerplate code, or worse yet, hundreds of micro-frameworks cropping up around Django to fill in perceived niches.

However the great thing about frameworks like these is that they are not DSLs. They are written in real programming languages, with all the power that implies.

If you do not want to use the framework's ORM layer, don't import it. Don't create models, and instead call into the database raw. Or use SQLalchemy's ORM.

If you do not want to use the framework's view class, don't import it. Create your own base view class, and subclass all your views from there.

The prudent programmer is one which wraps the framework classes in his own before extending.

Yes, superclassing does create great lock in for developers, but often lock in is what you *want*. you want to know that the code you wrote will take advantage of the platform that you wrote it for and not have to worry about the idiosyncrasies of all the platforms where it *may* run.

Seekerz

@Stefan Kanev, @SharpsawDotOrg

CQRS has a small initial investment but can help decoupling the domain logic and DAL (and do many other things as well)

http://blog.jonathanoliver.com/2009/10/dddd-why-i-love-cqrs/
http://www.blogcoward.com/archive/2010/03/14/cqrs-for-dummies-ndash-2-of-n-ndash-the-command.aspx

Jorge

"Make migration away from your framework difficult, or impossible."

But... it sounds like a bad thing if you say it that way.

Now seriously, I do write frameworks, but what I do is to create the interface, program against the interface, and create a class that implements the interface with virtual methods to give it a default behavior. The developer can choose to inherit or to implement the interface.

Anonymous Coward

It all depends, IMO mostly on the type of framework you design.

If your framework is a very small one, intended to provide a highly specialized solution to a very particular problem, instead of a generic application framework, designing around inheritance may be possible but unreasonable.

If your framework is a kind of a meta-framework, which builds upon other libraries and frameworks and which you use to build and extend a specific application, which relies on that framework and is by design intended to be strongly coupled to that framework and to nothing else, it may not make sense to try to avoid inheritance from framework classes.

I like to find reasonable analogies. Here's one (I think): some companies build families of smallish utility vehicles. One member of the family might be a small truck for spreading dung on the field. But on the same chassis and engine combination they might put a general purpose trunk with a small crane. Or a cistern for transporting fuel or water. Or a small cabin for transporting people around. Or just a larger crane. Or a platform for securing and transporting other vehicles in the same size category. That's IMO classical inheritance - every vehicle is still providing everything the bare chassis and motor would, but adds specialized functionality. However, the added functionality is strongly coupled to a particular type of chassis and engine. But that's OK, because the manufacturer doesn't intend to change the platform. And it makes sense for them, because overall this approach reduces cost and effort for both the manufacturer and the user.

Daniel Paull

Isn't the issue here reuse? It's far too common for (what the framework writers think is) reusable code to be stuffed into abstract base classes. A better approach is to put the reusable code in other places - functions, helper classes, etc.

In C++ the mixin idiom helps here as it gives the convenience of inheritance without all the bad things that generally comes with it. I wrote an article on this some time back that seems appropriate:

http://www.thinkbottomup.com.au/site/blog/C%20%20_Mixins_-_Reuse_through_inheritance_is_good

Other languages support mixin style programming too, but not all.

Cheers,

Dan

Stephen Souness

For those looking for examples of frameworks that have relied on your code extending their superclass, in the Java world consider:
- Controllers in earlier versions of Spring MVC
- TestCase in JUnit 3

In both cases annotations have replaced inheritance in later versions.

Jeff G.

- TestCase in JUnit 3

I think this is an interesting example. With JUnit <= 3, I was able to write tests using convention over configuration. Need to add a new test? Just add a method that begins with "test". Need to "assert" on something, just use one of the many inherited methods. Want to have some steps that occur before every test? Just put them in the setup() method.

I'm wondering if the 'flexibility' of JUnit 4 is offset by the need to configure, import, etc. Or is the additional complexity worth it?

There's something sad about having to annotate the tearDown method:

@After
public void tearDown() {

The comments to this entry are closed.