XP and others suggest that implementing everything the program OnceAndOnlyOnce
leads almost mechanically to good code. See also AbstractWithOnceAndOnlyOnce
- Somewhere there is a topic about when OnceAndOnlyOnce does not apply, I just cannot find it right now. For example, I sometimes find coincidental duplication where the duplication is merely happenstance, or at least not based on un-changing domain rules. Also, sometimes duplication allows independence of modules such that you can change one without worrying about breaking another, or even experiment with features in a new app without risking an older app. Somebody pointed out that capitalism and evolution requires duplication. As an absolute principle, I'd have to say "no". Its a rule of thumb.
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
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?
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
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.
go look at the OnceAndOnlyOnce again to see that it originated to describe a practice in external documentation, not necessarily programming practice.
- Just because two functions in different scope are named 'MakeRandomNumber?' doesn't mean they execute precisely the same code or even that they represent precisely the same black box functionality. In your example its quite clear that they don't. If I were to refactor the given code, it would take about 15 minutes: 12 minutes to determine that they weren't the same black-box and 3 minutes to change one of the names. That's an itty-bitty investment in refactoring.
- But let's say the functions *do* represent the same black box. When packages written by two completely separate groups are intermingled inside the same app, this is possible and even likely. The proper refactoring is certainly to unify the black box into a single package. In a one-team environment I'd expect the team to see the problem and fix it. In the simple multi-team environment, someone would extract the black-box to a third package usable by the other two. In the more typical multi-team environment, in which teams are likely to be isolated in space (and often in time), no such refactoring is likely to occur: That's one of the reason typical multi-team environments fail, in my opinion. Often, of course, in the real world the whole point is moot, since a given team works with other packages via interface only, i.e. they are sourceless and can do no such refactoring at all.
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?
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
), it can enable sharing, and it can explain. It is this last use of indirection that doesn't meet your criteria. The method:
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
It's a method called highlight that just calls a colour-reversing method on some block of text
- 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?.
- 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 W
ikiFlag here: MultiTeamExtremeProgramming
. -- TomRossen
Perhaps this relates to the "interweaving orthogonal factors" issue that I describe at:
The problem is projecting a multi-dimensional "shape" into the one-dimensional world of ASCII code.
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
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 S
impleMindededness. 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
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