Can Once And Only Once Loop

XP and others suggest that implementing everything the program OnceAndOnlyOnce leads almost mechanically to good code. See also AbstractWithOnceAndOnlyOnce.

Is it possible that a program can reach a state where implementing X OnceAndOnlyOnce causes Y to be implemented in two places, and vice versa, so that OnceAndOnlyOnce would loop?

Put another way, can a program reach a state where making one of two aspects "well-factored" perforce causes another aspect not to be, and vice versa?

In principle, can it happen? In practice?

Simple proof: OnceAndOnlyOnce for X may be mutually exclusive with OnceAndOnlyOnce for Y, by DeMorgansLaws.

~[(X ^ Y) v (~X ^ ~Y)] = (~X v ~Y) ^ (X v Y)

~[(X v ~Y) ^ (~X v Y)] = (~X ^ Y) v (X ^ ~Y)

One can optimize or (v) or and (^), but not both. QED.


If this is happening, it often means that the problem has a hidden CartesianProduct involved. Hopefully it can be eliminated using the BridgePattern or something else; failing that, examine the three possible implementations and decide which one has the least duplication.


Yes, I think refactoring one aspect of a collection of software can change the other well-factored aspects of the same code. For example, what if you had two class libraries, one very well suited for task A, say financial applications, and another well suited for task B, say statistical tools for scientific experimentation. Each has their own inherent architecture, their own abstractions and frameworks for invoking computations, etc.. And each might have a random number generator built in. Following the rule of OnceAndOnlyOnce, they quickly decide to combine them.

Along the way they uncover sizeable differences between the two capabilities, because of the emphasis of one package toward market prediction, the other toward accurate fine-grain control of random number generation to facilitate statistical analysis. Should they hybridize these two similar overlapping capabilities, creating a dependency between these previously independent libraries? Or maybe they could go look at OnceAndOnlyOnce again, to see that it originated to describe a practice in external documentation, not necessarily programming practice, and decide it is ok to just let things be. --ScottJohnston

This seems like a setup to me, on two grounds.

go look at the OnceAndOnlyOnce again to see that it originated to describe a practice in external documentation, not necessarily programming practice.

Unless, of course, they re-read the whole page and the many other pages that make reference to it. It's correct that the page originated about documentation, but that's a little irrelevant. It's easy to spot the references made by contributors to OnceAndOnlyOnce in programming terms. Further, if one reads the XP-ish wiki pages, the concept is most generally used re: programming practice, not external documentation practice. -- MichaelHill

Yes, it was a setup, an example made up in attempt to illustrate a point. Where I've come across this in practice would take a lot longer to present clearly, but the jist would be the same. And I was thinking of redundant sets of functions for generating random numbers, probably without common architecture or symbols. Different ways of setting the random seed, different ways of generating vectors of random numbers, different ways of selecting and parameterizing random distributions.

Anyways, I think I'm talking about the same thing someone else has said around here, of listening to the code more than listening to a preconceived notion of how the code should be. Perhaps YouArentGonnaNeedIt can apply just as well to a refactoring activity as a code generation one. Don't get me wrong -- refactoring is a beautiful thing. But in my experience, when working with software foundations laid down by others, tolerance for yet-to-be-refactored code can be a boon to projects, by keeping the focus on customer requirements. As long as you're willing to go back in later, to carry on with incremental improvements and refactoring where it seems most appropriate, after the heat is off. --ScottJohnston


One of the main ways we listen to the code is to observe the same code in more than one place. We take this as a suggestion that the multiple occurrences of the same code should be consolidated into one. Under what circumstances would this be inadvisable?

Note, however, that the Extreme rules regarding refactoring are specifically stated to be part of generating our own code. To my recollection we have made no assertion that you should take other people's libraries and refactor them. --RonJeffries


Also please note that the original question isn't about one's opinion as to whether refactoring is a good idea. The question is whether there can exist a configuration of code such that application of the OnceAndOnlyOnce rule can loop.

People will usually stop refactoring before the code is perfect, in the real world. But can the rule, driven to the limit, fail ... or does it always stand as an ideal, more or less well attained? This is the wrong question, as it is a false dichotomy. The rule can indeed loop, because programming languages are not infinitely expressive. Therefore, in any real programming language, you'll encounter occasional situations where you'll need to allow duplication. But you still try to manage the duplication. You put the duplicated parts close together in the same module or make sure that the duplicated text will never need to be changed, and you provide ample documentation as to what's going on. And new language features can allow you to remove duplicated code, because they make the language more expressive. OnceAndOnlyOnce driven to the limit can loop; but it still stands as an ideal.

The rule cannot loop. You are always striving for 1) less duplication and 2) more communication. These are both monotonic functions. I do a refactoring. There is now either less duplication or more communication or both. When there is no more duplication and there is nothing more I want to communicate, I am done.


I guess my example didn't work as I hoped, perhaps because I used a somewhat personal interpretation of what "well-factored aspects" meant without fully disclosing it. In my view well-factored code is code where every generic capability gets used more than once, and any generic capability that gets used only once gets either merged with its peer mechanisms or used again by some other code. So instead of OnceAndOnlyOnce rule, I follow a UsedAtLeastTwice? rule.

Which is why they have ThreeStrikesAndYouRefactor.

This allows for separate implementations of almost the same thing to live side by side indefinitely, as long as they have been adequately leveraged by other more application specific code. And it gives more time for the programmer to experiment with alternates, to consider which of two or three different approaches proves easiest to use/understand/communicate in the long run. Once all the ramifications have been theoretically and empirically worked through, the programmer can undertake the sizeable refactoring with confidence. The RalphJohnson patterns on evolving frameworks goes over some of this. Now I know this discussion doesn't belong here, in fact the above paragraph seems to say no further discussion is required at all, but the way I read the original question was close enough to something I wanted to say, so I did. --ScottJohnston

When you see what the bottom part of this page should be called, make a page for it and leave a pointer.

I think what you are getting at is that you don't want indirection that doesn't pay for itself. Indirection can pay for itself by creating isolation for a piece of code (reducing the risk that a change there will have an effect here), it can enable sharing, and it can explain. It is this last use of indirection that doesn't meet your criteria. The method:

 highlight: aRectangle
  self reverse: aRectangle

is a good idea, even if it is only called in one place. --KentBeck
I feel greatly handicapped jumping in here for a couple of reasons

  1. The wiki (this wiki, which is The Wiki, hallowed be its gardening) may be in the PerpetualNow, but I'm stuck in linear time. I feel like I may be trying to have a conversation with an alien race that disappeared eons ago, ala TheMartianChronicles?.

  2. I speak only the XpInferiorLanguage? Java and don't understand Kent's code example.

That said, here's my two cents: maybe polymorphism/extract method would solve Scott's problem: the two methods have the same name because they are almost the same. So extract/abstract that which is shared to a separate method/class.

This problem is orthogonal to the "multi-team environment"/"refactoring other peoples' libraries" issue, which I feel is actually a more significant one for XP, so I'm planting a WikiFlag here: MultiTeamExtremeProgramming. -- TomRossen


Perhaps this relates to the "interweaving orthogonal factors" issue that I describe at:

http://www.geocities.com/tablizer/struc.htm#if

The problem is projecting a multi-dimensional "shape" into the one-dimensional world of ASCII code.


See CircularRefactoring


CategoryExtremeProgramming
EditText of this page (last edited August 23, 2005)
FindPage by browsing or searching

This page mirrored in WikiPagesAboutRefactoring as of April 29, 2006