Final classes by default, why?

Posted on by Matthias Noback

I recently wrote about when to add an interface to a class. After explaining good reasons for adding an interface, I claim that if none of those reasons apply in your situation, you should just use a class and declare it "final".

PHP 5 introduces the final keyword, which prevents child classes from overriding a method by prefixing the definition with final. If the class itself is being defined final then it cannot be extended.

— PHP Manual, "Final Keyword"

An illustration of the final syntax for class declarations:

final class CanNotBeExtended
{
    // ...
}

class CanStillBeExtended
{
     // ...
}

class Subclass extends CanStillBeExtended
{
    // override methods here
}

// Produces a Fatal error:

class Subclass extends CanNotBeExtended
{
    // ...
}

For a couple of years now I've been using the final keyword everywhere (thanks to Marco Pivetta for getting me on track!). When I see a class that's not final, it feels to me like it's a very vulnerable class. Its internals are out in the open; people can do with it what they want, not only what its creator has imagined.

Still, I also remember my initial resistance to adding final to every class definition, and I often have to defend myself during workshops, so I thought it would help if I explained all about it here.

The alternative: non-final classes

Omitting the final keyword is the standard for many developers, who reason that they should allow others to reuse the class and change just a part of its behavior by extending it. At first this may seem like a very considerate thing to do, but there are several downsides to it.

You are the initial designer of the class, and you had one particular use case in mind when you created it. Using the class for something else by overriding part of its behavior may jeopardize its integrity. The state of the extended object may become unpredictable or inconsistent with the state of the original object. Or the behavior may change in such a way that the subclass can no longer be considered a proper substitute for the base class.

Furthermore, the developer who creates a subclass of your class to override its behavior, probably doesn't need all of the internals of the parent class. Still it inherits those internals (data structures, private methods), and will soon arrive at the point that it has to work around them.

In terms of future development there's another issue: the class that has now been subclassed, still has a life on its own. Its clients may have different needs over time, so changes will be made to it. These changes may affect the subclasses too, since they likely rely on a particular implementation detail of the parent class. When this detail changes, the subclass will break.

From all these issues we have to conclude that by allowing subclassing, we will end up with an undesirable situation. The future of the subclass and the parent class will become intertwined. Refactoring the parent class will be quite hard, because who knows which classes are relying on a particular implementation detail. And since the subclass doesn't need all of the parent class's data and behaviors, they may act like they are the same kind of thing, but in reality, they aren't.

Replacing is better than overriding

So changing the behavior of a class shouldn't be done by subclassing that class and overriding methods. But what else can we do? In most cases, actually modifying the code of the original class isn't possible. Either because the class is used and relied on elsewhere in the project, or it's not physically an option to modify its code because it's part of a vendor package.

I'd even say that changing code shouldn't be the preferred way of changing behavior in the first place. If you can, change the behavior of an object by reconfiguring it with, that is, by swapping out constructor arguments, you should do it.

This reminds us of the Dependency inversion principle, according to which we should depend on abstractions, and the Open/closed principle, which helps us design objects to be reconfigurable, without touching its code.

What about the Template Method pattern?

There's an alternative approach for changing the behavior of a class. It's a behavioral pattern described in the classic "Design Patterns: Elements of Reusable Object-Oriented Software". The pattern is called "Template Method":

Define the skeleton of an algorithm in an operation, deferring some steps to subclasses. Template Method lets subclasses redefine certain steps of an algorithm without changing the algorithm’s structure.

This solution is slightly better than allowing subclasses to override a parent class's behavior, since it limits what can be changed. It renders the parent class itself useless by marking it as abstract. This prevents it from having a life on its own. In the Symfony Security component we find an excellent example of the Template Method pattern in the abstract Voter class. It helps users implement their own authorization voter, by providing the bulk of the algorithm that is supposed to be the same for all voters. The user only needs to implement supports() and voteOnAttribute(). which are both smaller parts of the overall algorithm. Symfony wouldn't be able to guess these implementations, since they are project-specific.

abstract class Voter implements VoterInterface
{
    public function vote(TokenInterface $token, $subject, array $attributes)
    {
        $vote = self::ACCESS_ABSTAIN;

        foreach ($attributes as $attribute) {
            if (!$this->supports($attribute, $subject)) {
                continue;
            }

            $vote = self::ACCESS_DENIED;
            if ($this->voteOnAttribute($attribute, $subject, $token)) {
                return self::ACCESS_GRANTED;
            }
        }

        return $vote;
    }

    abstract protected function supports($attribute, $subject);

    abstract protected function voteOnAttribute($attribute, $subject, TokenInterface $token);
}

It's really nice, but still, there's nothing in here that couldn't have been solved with composition instead of inheritance. An equivalent solution would've been:

// The user needs to implement this interface, instead of extending from Voter:

interface AttributeVoter
{
    public function supports($attribute, $subject);

    public function voteOnAttribute($attribute, $subject, TokenInterface $token);
}

// The Security package provides this standard Voter implementation:

final class Voter implements VoterInterface
{
    private $attributeVoter;

    public function __construct(AttributeVoter $attributeVoter)
    {
        $this->attributeVoter = $attributeVoter;
    }

    public function vote(TokenInterface $token, $subject, array $attributes)
    {
        $vote = self::ACCESS_ABSTAIN;

        foreach ($attributes as $attribute) {
            // We delegate calls to the injected AttributeVoter

            if (!$this->attributeVoter->supports($attribute, $subject)) {
                continue;
            }

            $vote = self::ACCESS_DENIED;
            if ($this->attributeVoter->voteOnAttribute($attribute, $subject, $token)) {
                return self::ACCESS_GRANTED;
            }
        }

        return $vote;
    }
}

Following the reasoning from "When to add an interface to a class" we might even reconsider the presence of a VoterInterface in the example above. If all the voting is done in the same way, that is, the voting algorithm is the same in all situations, we don't actually want users to write their own implementations of VoterInterface. So we also might provide only the Voter class, not the interface (I'm not actually sure it's the case for the Symfony Security component, but it's a good design choice to consider in your own projects).

The proposed solution doesn't involve inheritance anymore. It leaves the internals of all the involved classes to themselves. That is, these classes can all be final, and can be safely refactored now. Another advantage is that we could reuse each class in a different scenario. Final classes are more like building blocks ready for use, instead of templates that still need to be made concrete.

Composition over inheritance

You may have heard the phrase "Composition over inheritance" before; this is what is meant by that. In most cases, you should prefer a solution that involves composition over a solution that involves inheritance. To be more specific, if you feel the need to reconfigure an object, to change parts of an algorithm, to rewrite part of the implementation, consider creating a new class instead of overriding an existing class. If you need to represent a hierarchy of classes, where subclasses are proper substitutes for their parent classes, this would be the classic situation where you may still consider inheritance. However, the result may still be better if you don't inherit from concrete parent classes but from abstract interfaces. Still, this is very tricky to get right (I personally regret most of my previous work involving inheritance), so if you can, you should still prefer composition.

Extension should be a distinct use case

As a class designer you create a class in such a way that it provides a number of useful things its users can do with it. For example, they can:

  • instantiate it,
  • call a method on it,
  • get some information from it,
  • or change something about it.

"Reconfiguring it to change part of its behavior" should also be on this list, as a separate item. And so should "allowing it to be extended to override part of its behavior". It should be a deliberate decision to allow the user to do that with your class. Allowing a class to be extended has (often limiting) consequences for its design, and its future. So at least you shouldn't allow it by default.

"Final" pushes everyone in the right direction

So if allowing users to subclass a class shouldn't be the standard, then not allowing it should be. In other words: adding final to a class declaration should be your default. This simple trick will lead everyone in the right direction: towards classes that are smaller, have fewer maintenance issues, are easier to refactor, and act more like building blocks that can be reused in different parts of the project.

"You can always remove final if you want to"

I've often heard one argument for using final that I wanted to address here: "You can always remove final if you want to." In a sense, this is right; you can always allow extending a class whenever you want. But the same seems to be true for adding "final" - it's always just a few keystrokes away. As discussed in this article, I don't think it's the right attitude; it's better to close down, instead of open up. Opening up after having been closed is asking for all the trouble of inheritance described above. Make sure that instead of removing "final" from the class declaration, you will always aim for a solution that replaces part of the existing solution, and uses composition to allow for reconfiguration.

"My mocking tool doesn't work with final classes"

One objection against final classes that I've often heard is: "I like what you say, but my mocking tool doesn't work with final classes". Indeed, most don't. It makes sense, because the test doubles that such a tool generates usually look something like this:

final class CanNotBeExtended
{
}

// This produces a Fatal error:

class TestDoubleForCanNotBeExtended32789473246324369823903 extends CanNotBeExtended
{
    // override implementations of parent class here...
}

It's unfortunate, but the tools are not to blame. They'd have to do some sneaky things like hooking into the class loader to make the class non-final (in fact, that's quite doable). But they don't, so the truth gets slapped in our face. All that the tool is saying is that a final class can't be extended - there's nothing problematic about that fatal error. Instead, whenever we feel the need to replace a final class with a test double, we should consider two options:

  1. Maybe we shouldn't want to replace the real thing, i.e. the class.
  2. Maybe we should introduce something we can replace, i.e. an interface.

Option 1 is often useful when you're trying to mock things like entities and value objects. Just don't do it.

Option 2 should be applied in most other cases. When the class should've had an interface, add one, and create a test double for the interface instead.

Conclusion

In conclusion: make your classes final by default. One trick to help you with that is to modify the IDE template for new classes to automatically add final for you. Also, make the "final" discussion part of your technical code review. Ask: why is this class not final? The burden of proof should be on the author of the class (and if they don't agree, talk to them about the merits of using "final").

PHP design reuse package design interfaces
Comments
This website uses MailComments: you can send your comments to this post by email. Read more about MailComments, including suggestions for writing your comments (in HTML or Markdown).
Daniel Restrepo

Does it improves PHP performance?

Matthias Noback

No idea!

Tomáš Votruba

FYI, mocking with final classes is easy - include https://github.com/dg/bypas... in the start of tests

Matthias Noback

I'm not sure I'd recommend this, since you really shouldn't need to mock final classes. Anyway, if you're dealing with some kind of mess, as a last resort, maybe it's okay to use this :)

Tomáš Votruba

This "mess" is 90 % mocking I see in projects. Also it's the #1 reason by people don't use final.

Mocking tools should be shipped with the "bypass" by default, that would solve most of this crappy code :)

Sebastiaan Stok

FYI. Symfony uses interfaces for a lot of things, including the ResolvedFormType Factory. At first I was skeptical about this overuse of interfaces, but some really interesting features have been added to the Profiler that would not have been possible without these interfaces! They are in this specific case used as decorators that collect information.

The same goes caching and test-only replacements. Even though an alternative implementation is not expected, it doesn't mean interfaces should not be considered.

Otherwise the existing code would have to be changed immensely to support these use-cases, which would in turn violate to many principles.

ludofleury

There is no discussion around that for me. Same a private properties as default scope. Final is the way to go (when your tooling support it).
- its defensive programming (so it can’t be bad)
- Vertical inheritance as a first design bring the classic banana-monkey-jungle issue
- It is not against SOLID (the O).
- Non final is echoing with “premature abstraction”, like you are already expecting someone to extend your class.

Restricting the scope of your code to the bare minimum will save your life on the next refacto.

With our collaborative tooling (github, bitbucket etc), as soon as a PR land with a scope extension (removing final or switching to protected), there you start a discussion about why/what/how.

Regarding the open source lib. Its the right call. You do not control the usage of your lib... so you should close all doors to be able to open new ones later without breaking an app where someone extended your class.

I had the mentioned issue with an open source lib (money php): a final class that I would really have love to extend, here’s the 3 steps from the gentlemen addressing open-source:

1- Open a issue, even better submit a PR
2- Discuss the design with the owner
3- If the owner doesn’t want to risk maintainability just for your use-case, fork & update your composer.json to fetch your fork. (It should take you 3 min since you just want to remove 1 php token)

I dont think it a matter of taste or a doctrine. Its extremely cheap, revokable, ease maintainability = its good coding practice.

NB: and honestly I wish that code courses/school recommend that during the OOP chapter after the infamous Animal-Cat/Dog which led me to some awful chain of doom as a junior dev.

Robert Sagat

This trend is pure hubris. What inevitably happens with these "everything should be final/private" libraries is something breaks and now a developer is forced to copy/paste entire classes and replace them just to fix your one little bug instead of just extending the class and overriding a single function. Hell in many cases you don't even have to rewrite the function, you're just adding something before or after a parent call. Some frameworks even provide automatic before/after hooks for wrapping any class methods, but this feature is often broken by final and/or private.These otherwise make for very maintainable ways to extend or fix library/dependency code when you really need to. Copy/pasting entire classes is as unmaintainable and frustrating as it gets, and often leads developers to just stop updating the library (even when there's no functional reason they can't, it's just that YOU have made things difficult).This whole thing just comes off as just downright haughty. "I wrote this and it is perfect and you're not allowed to do anything other than what I intended with it." This is the developer prioritizing themselves over their peers. If you want to be clear what other devs really should/shouldn't do then you can be diligent in your comments and @internal/@api type annotations.

ludofleury

Hey,
I’m sorry you feel that way. For open source libs it happened to me and I posted a comment to this article about it.

Personally, when I write final on my classes, most of the time it means: ‘I wrote this specifically for this feature/story/bug fix, I dont trust myself about tomorrow because we are far from (business & technical) model maturity, so I have no clue about the cost of the tech debts if you start to inherit. Do not trust me’

So not only it ease your maintainability ( i wont break your inherited tree on my next refacto ) but it also ease mine (I wont have any doubt while refacto a class marked final, this is twice as true if it’s a open source lib that unknown people are using.)

Regarding docblock tag as design rules, I tend to runaway from them when the language provide a strict way to enforce them.

Disclaimer: I work in startups only in agile context, maybe it would be different on V cycle close-scoped project.

Laurent Lacôte
And I targeted the wrong post to answer...
I mixed up your post with a post from the actual blog's author (Matthias). Sorry for that. XD
Laurent Lacôte
Hi ;)

So, first of all thanks for this writing, which I stumbled upon through search engine with loosely related keywords. ^^
Always interesting to have clear cut point of view on a design practice.

With that said, I have to say I'd tend to agree with Robert Sagat here, although he is into his own extreme.

I definitely understand your point about final expressing the idea of "I don't want to to tweak this because it could lead to hard to track inconsistencies in both ends as each one evolves".

The thing is, the way you wrote your post, you're making it sound like it's the all-end graal for every class, borderline saying you should always use that instead of interfaces or classic hierarchy.

But what you are speaking about here is not at all composition. You're saying yourself, it's copy/pasting.
Composition would be either using Traits (which have their own limitations and shortcomings) or using a combination of smaller Interfaces with the Template pattern.

Saying "I make my class final. Anyone that would have loved to reuse some of my witty algorithm or just change a few details on a process that otherwise entirely fits their need will just have to copy it"... Is like trying to revive the hell of eighties...

In short, yeah, final has its uses, and it's probably underused, but it's just one component among many that should be considered when designing an app, not the "by default choice".

IMHO at least. :)
Have a nice day!

    Laurent
Matthias Noback

Thanks for joining the discussion here!

Adding something before/after a method call is an ideal candidate for using composition. Final isn't in the way. If a framework intends to provide "hooks" like you mention, they will be using this template method pattern, in which case, it isn't that bad, because they consciously provide this option. Again, "final" isn't in the way (because the maintainers allow extension anyway).

Well, I guess, instead of trying to refute your arguments, I would have to add this: I have _never_ needed to override a particular method from a library/framework class, let alone, be limited by the fact that it's final (since most implementers don't even follow this practice anyway). But to be fair, maybe your experiences are different from that. Something that _has_ been a problem with third-party solutions is that they sometimes don't support dependency injection, or don't come with interfaces, which makes it impossible to replace/inject things. So maybe here's where we arrive at the point where my advice to use final always only applies if you also make your code Open for extension/Closed for modification, if your classes apply the Dependency inversion principle. However, even if they don't, I'd start making things final, because they show where the class design needs more flexibility (whenever you feel the need to extend, you should allow an _extension point_ instead, e.g. a configuration option, or a way to inject some other dependency).

Gaetano Giunta

I fully agree with both Robert Sagat and Fabian Wesner on this topic.

First off, I do agree that composition should be preferred over inheritance for most cases - albeit not all. Better support for Traits in php would make me a very happy camper.

Second, I am extremely surprised to hear that Matthias has never had to override a particular method from a library/framework class. In my experience, I extended/modified methods from framework/library classes in a good 30% of the cases where I use them. There multiple reasons to that, broadly categorized in a) adding a workaround for a bug that the author won't/can't fix in a timely manner, and b) adding a new feature that is either not generic enough for the upstream lib or that the author dislikes.

In all those cases, I have politely tried upstreaming changes when applicable, and kept them in house when not. One thing I never did though is to complain to the author when new versions of upstream broke my extensions - as long as the changes were done to the internal definition of the classes (that's what public/protected are there to tell me).

Making classes private by default means that the work will actually increase for every one:

  • more work on my side to copy paste original class instead of extending it
  • more work on my side when upstream releases an update to do a full diff and merge, where 90% of time upstream updates are compatible with my changes
  • more work on author side as I will have long discussions with him asking to add all the features and fixes that I could have added locally with non-final classes
  • more insecure apps as I will not be updating my copies of upstream libs as frequently as before

@ludofleury: ‘I wrote this specifically for this feature/story/bug fix, I dont trust myself about tomorrow because we are far from (business & technical) model maturity, so I have no clue about the cost of the tech debts if you start to inherit. Do not trust me’

You don't need to mark your classes final for that: a) I don't trust you anyway; b) everything you mark protected conveys the same meaning; c) you are not responsible for my technical debt d) if your library is all in beta state you should simply release it as version 0.x and I will know that it is in flux

It seems that no one here is bringing up the difference between in-house code and public code. For in-house code, you are free to apply whatever rules you like - trust your developers more and make everything protected by default or the opposite. But please, for public libraries, do not treat the rest of the os world as idiots.

Mihai Stancu

Hi Matthias,

Languages that have package level (ex : java / c#) visibility qualifiers to specify if a whole class is public or not change the ballgball here*.

For example instead of using final you'd mark the class as protected and only your package will be able to extend & overridde it.

This alleviates the mocking problem because you can include tests in the package and gain access to extend the class.

In GoLang this goes a bit further, everything within a package is by default internally accessible and you specify structures or methods you want exported.

Test cases are easier to isolate without reflection as long as you can test a void method that alters the internal state and can thr directly access the state to check it.

The premise here is that the developer of one package should be able to manipulate internal state coherently thus making the encapsulation unit the module not the class.

*I am not a java / c#, i have very limited understanding of what those qualifiers do overall.

ludofleury

I miss so much class visibility in PHP :(

Matthias Noback

Thanks for explaining this here!

Fabian Wesner

Hi Matthias,

at Spryker we discussed this topic a lot and decided against it. Now all our core classes are not final and we even decided to use "protected" visbility instead of "private" for non-public methods.

In general any extension of a internal classes should be avoided. That's why Spryker has an extensive plugin system and all business logic is hidden behind facades. But in some case our users must do small changes where we did not implement hooks. We tell them that they take over full responsibility in this case and that they need to switch from the caret "^" to the tilde "~" in composer.json because even minor releases can result in compatbility problems then.

So when they still decide to extend an internal class then they can do either Composition or Inheritance. I know the theory behing "Composition over Inheritance" but in practice Inheritance works just better for the users. This has two reasons. First the amount of code that appears in the sub-class is much smaller and is focussed only on the change itself. With Composition all public methods need be there and delegate to the underlying vendor class. It's even worse when the user wants to change something in a non-public method. In this case he needs to copy over a huge part of the class just to apply a small change. Second I experienced that extensions which are using Inheritance are more stable when the vendor class is changed. For instance when a method is added then the new method is simply inherited and everything just works. With Composition the method needs to be added manually.

Best

Fabian

Matthias Noback

Hi Fabian, thanks for sharing your experience here! Very interesting points. It's nice that you mention the trade-offs involved.

RJ Garcia

Regarding "My mocking tool doesn't work with final classes". This was a big blocker for me when I designed with anemic models and had all of my business logic in my services. I was always in a situation where I needed to mock various services so that I could test a certain part. But when I moved to more richer models, my need for using an actual mocking tool has become much less.I typically have now just a base test class with methods like createBook($defaultParam1 = null, $defaultParam2 = null), createReview($defaultParam1 = null, $defaultParam2 = null). Then use actual built entities for testing domain logic (instead of mocks).It's made the code so much easier to test and develop.

Pavel Bisse

But you can still implement an interface and mock this one.

RJ Garcia

You could, but if you think about it, an interface for core business logic doesn't always make sense, in fact most of the time, there's only one way to accomplish most business processes.

When you start creating interfaces just for mocking you'll end up mocking your entire business layer which ends up making your tests more brittle because you'll need to be keeping your test mocks in sync with the behavior it's supposed to be mocking.

Pavel Bisse

I would never create an interface only for mocking. My interfaces are a medium to identify an implicit object type. Since php 7 you can use an anonymous class as a stub, which implements the same interface you final class does. At least you could even use composition in your test and the code is better readable + anonymous classes are faster as an mocking framework. Inheritance is a huge part of software development but in 90% people using it as a pseudo flexibility.

Matthias Noback

Thanks for sharing!

David Grudl

ad mocking of final classes: it solves https://github.com/dg/bypas...

Simply call DG\BypassFinals::enable()

Marco Pivetta

Please don't mock final classes: they are final because that is the only possible implementation for that type, hence no alternatives (including mocks) are possible. If there was a different possible implementation, there would be an interface for that type.

Roman O Dukuy

All my service in application is final. And I can write only function tests and this is create some broblems in full coverage. Becouse I i'm used to buth test unit and functional.

Marco Pivetta

If you have interfaces (as you should), this is a non-issue.

David Grudl

This is true in an ideal world where all the code is written correctly. (Or in Dart thanks to implicit interfaces.)

Marco Pivetta

This is true when the code is not written lazily. Laziness is fixed by actually doing the work we're supposed to do.

Gaetano Giunta

@marco "Laziness is fixed by actually doing the work we're supposed to do." I see it differently. The work you are supposed to do is to allow fellow developers to interact with your code and cope with the amount of noise they generate in your support forums instead of treating them as lesser beings. If you don't want want your code to be ever changed, don't write oss, keep your code private and expose an API :-P

Pavel Bisse

-> When the class should've had an interface, add one, and create a test double for the interface instead.

In some projects I also used an anonymous class, which implements the given interface.
At least it's kind of a composition in your tests :)
Something like:

$search = new Search(new class() implements GroupFacadeInterface {});

Nice article by the way.

Marco Pivetta

Just cross-referencing https://ocramius.github.io/... - if you got to the comments section and you aren't yet satisfied by the reasoning provided by Matthias, give this a read.

bobbitus

I've had the questionable joys of working on a project where lib with classes by ocramius, whos be advocating this for a while now has been used.
We ended up copying whole classes from him, just so we could do some minor changes, because of this kind of behavior.

Marco Pivetta

Could you please clarify where this happened? If it's final in one of my projects, it usually has an interface to allow for composition.

Dmitri Lakachauskis

Two notes:

If you use Doctrine, you can't make your entities final.
If you want to configure lazy services i.e. with symfony/proxy-manager-bridge, then you're out of luck too.

Marco Pivetta

You can make interfaces lazy by manually operating with ProxyManager in a compiler pass. This is also already supported out of the box in symfony, but not yet documented: if you look at the DIC XSD definitions, you can now specify an interface attribute on a service definition for a lazy service.

Matthias Noback

Thanks for joining the discussion @ocramius:disqus ; you were the one who started it for me anyway :) It's good to know that there are some solutions there for technical limitations regarding the use of "final".

Florent Morselli

I agree with you. That’s why I also proposed a dedicated section to the jupeter/clean-code-php repository few months ago (https://github.com/jupeter/....

Dmitri Lakachauskis

Aha, good to know. However, can't find this new interface attribute you're referring to - https://github.com/symfony/...

Marco Pivetta

See symfony/symfony#27697

Dmitri Lakachauskis

Ah, I see. "lazy" is now a string. This feature is not released yet, so technically I was correct :) In any case I totally support your article and obviously this one. In my recent projects I use final by default.

Nikola Poša

Good one! I especially like this statement: "Final" pushes everyone in the right direction. And I believe this applies not only to classes themselves, but class methods, too.Illustration you have in 'Template Method pattern' section is a good example where finalization approach can be applied to methods. In this particular case, vote() method could have been defined as final so that it sends a clear message to a developer and guides him in the right direction. Almost as if you told: "This is how I imagined voting to work, and in order to complete it you just need to implement those abstract methods. If you don't like it, you can always create your own VoterInterface implementation".

Matthias Noback

Thanks! Yes, indeed, Voter could remain "abstract", with just one "final" method. The way it is now is ambivalent: do you want to allow users to extend this method, or not?