XP and others suggest that implementing everything the program OnceAndOnlyOnce leads almost mechanically to good code. See also AbstractWithOnceAndOnlyOnce.
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.
Both of these can be expressed as (X xor Y), which expresses both X and Y OnceAndOnlyOnce. If your language doesn't include 'xor' (or the language extension capabilities to form it), perhaps you're at an impasse. Regardless of this fact, I'm having some difficulty seeing how the above proves -anything- about programming language expressions. All I see when I look at them are rather meaningless propositions. These need to be associated in some structural manner with abstractions over language expressions if this proof is to mean anything at all.
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.
The BridgePattern simply pushes the management of duplication off into another gizmo (for good or bad). Each combo may still have duplication in relation to other combos, and there is probably a point where the interface scaffolding to eliminate all duplication is larger and more complex than the duplication itself, or perhaps we will trade implementation duplication for interface 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 (and ignoring BestPractices involving CouplingAndCohesion -- db), 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.
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 do not think you mean 'monotonic', I think you mean 'wellfounded' (at least, wellfoundedness is the essence of termination of loops). It's not clear that there is any wellfoundedness here. Increasing communication alone seems like it can go on forever, because there are so many interesting ways of looking at a program and it's rarely possible to communicate them all. Code that is written to emphasize its correctness does not necessarily demonstrate clearly its runtime performance, and conversely optimization (which could be seen as the clarification of runtime performance) often obscures the correctness of the original program. Sometimes reducing duplication can only be achieved with additional plumbing whose complexity causes a maintenance burden which outweighs the benefits of reducing the duplication in the first place.
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: aRectangleis 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
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.
The SimpleMinded answer to the basic question is "yes". Why this can be and or should be done as well as how to do it, is up to you. -- DonaldNoyes 20071005
Another SimpleMinded answer to the basic question is "no", and why it can't be or shouldn't be done is up to you. What good is a SimpleMinded answer if you cannot utilize reason to assert that it is correct (or why)?
What is meant by "up to you" portion of the SimpleMinded answer only intimates that as loops are iterative structures, with variables determining how many OnceAndOnlyOnce loops you will exercise and upon what. You can catch a glimpse of what this means by the myriad illustrative comments which populate this page. Since loops are general purpose and only determine the number of times the elements in a series of artifacts, be they arrays, databases, collections, aspects, approaches, frameworks, classes, capabilities, interfaces or documents are processed. how it should be done is up to the implementor. What may complicate the consideration is the matter of identity as to what OnceAndOnceOnly is defined to mean. The proof is only that loops are functional, and can be made to work. If one chooses to limit the loop to an exercise of one or none, as you indicate it is up to the one who can not or should not do it to work out how and why it can not be done. To me, all working solutions are inevitably the result of SimpleMindededness. The statement of problems which cause confusion are often the result of the lack of it.
I think you're misinterpreting the question: can OnceAndOnlyOnce result in an infinite loop, such that the implementation of one component OnceAndOnlyOnce requires the implementation of another component AtLeastTwice, and vice versa? This isn't a question of decisions or -choice- to artificially limit the loop 'count', or any sort of 'mindedness' at all.
That may well be, but you will also note that I said "What may complicate the consideration is the matter of identity as to what OnceAndOnceOnly is defined to mean." I may add ItDepends also on what you mean by "Loop". There are many different kinds of loops, including infinite interation. I maintain that well structured loops include limits and/or completion strategies which prevent "infinite" recursion. -- DonaldNoyes 20071008
Nobody will argue that the answer to the question of CanOnceAndOnlyOnceLoop will depend upon how one operationally defines OnceAndOnlyOnce, but a focus on "loop" seems to be so much semantic hairsplitting. Your own assertion, that "well structured loops" must inherently prevent "infinite" iteration or recursion, results in an undecidable description for "well structured" equivalent to the HaltingProblem - i.e. in the general case, deciding that a loop is not "well structured" will itself require an "infinite loop". I'm not convinced of your wisdom in maintaining that position. In any case, by your pet definition, the question of whether refactoring expressions to occur OnceAndOnlyOnce is possibly an "infinite" loop should translate to: "is this necessarily NOT a well-structured loop"?
See CircularRefactoring WhatIsOnceAndOnlyOnce SuccessOrientedApproach