Re: Advisability of circular dependencies in packages
by jbert (Priest) on Nov 13, 2006 at 16:34 UTC
|
On the subject of bringing old code up to snuff.
Well structured code has good interfaces between sections, hiding detail. The responsibility of a section and its underlying assumptions is understood in terms of it's interface and accompanying documentation. (OK, yes, abstractions are partially leaky in practice, but to the extent that they aren't, you win).
Where you lack this, you need to read and attempt to understand much more code to get the picture of what is going on.
One way you can evolve a codebase is to try and identify candidates for such interfaces (configuration and logging are often a good place to start). If you find somewhere where you can seperate off a chunk of functionality behind a decent interface, you can write your new interface in terms of the old functionality, i.e. you can proxy back to the existing (proven) code whilst gaining the benefit of having clearer expression of intent in your newer code.
You can document and unit test these interfaces, etc and get benefits that way.
Over time, as you touch older code, you might migrate it over to the new interface, reducing the amount of code which depends on the old codebase directly.
Possibly, you could re-implement the interface in terms of new, clean code (relying on your tests to see if you've broken anything). You can then either ditch the old code (if no-one else is using it) or proxy the old methods onto the new code if possible.
So, I think a large part of design (including retro-fitted design) is coming up with good interfaces between sections of code. The above is one way of retro-fitting such design on existing code.
Also - IMHO an interface should provide the least functionality you can get away with for the task at hand. If you have an internal function (_foo) which you think might be handy to make public, resist the temptation - the smaller the interface the easier it is to understand.
If you do want that function in the interface in the future, it's easy enough to rename (because it's only used internally in that module). The reverse isn't true. Taking a method private (say because you want to change an internal algorithm) is much harder - you have to find and change all occurences of the code which uses it (or come up with a reasonable way to fake it in the new implementation). | [reply] |
|
|
jbert: ++
Very good post--especially the last two paragraphs! I wholeheartedly agree that smaller interfaces are better interfaces. I wish more people had this view. It's much easier to build clean structures with clean, simple interfaces (think Lego) then large interfaces (Windows API, Swing, etc.). You can *always* add new functionality, if required, but it's much harder to remove functionality from an API.
--roboticus
| [reply] |
Re: Advisability of circular dependencies in packages
by roboticus (Chancellor) on Nov 13, 2006 at 15:23 UTC
|
throop:
My take on it is that for the long term, it's a Very Bad Idea(tm). Circular dependencies in code usually indicate that parts of the code (as you mention) are in the wrong place.
For the short term, however, it's not a bad thing. If you're repairing code, then I'm a fan of slowly improving the codebase step by step rather than changing everything willy-nilly. Take the most annoying problem, and fix it. Then polish up the edges a bit. Rinse-lather-repeat.
Circular dependencies may not be your worst problem, so it's not necessary that it be the first cleanup you make. However, if it were me, I wouldn't stop the process until I got rid of them.
...just one robot's opinion...
--roboticus | [reply] |
Re: Advisability of circular dependencies in packages
by Old_Gray_Bear (Bishop) on Nov 13, 2006 at 16:27 UTC
|
Short term, OK-ish; long term, Madness.
Get a copy of Peter Scott's "Perl Medic, Transforming Legacy Code". It directly addresses the issues of controlled change (how to refactor without breakage), developing test harness for code that wasn't written to be testable, developing documentation and strategies for keeping it current, the whole nine meters.
----
I Go Back to Sleep, Now.
OGB
| [reply] |
|
|
Seconded. I've just started reading Perl Medic, and I'm thoroughly enjoying it. While I don't know if there's a specific chapter on dependency refactoring, a good test harness will definitely help you with that.
| [reply] |
Re: Advisability of circular dependencies in packages
by pileofrogs (Priest) on Nov 13, 2006 at 19:34 UTC
|
I'm not really educated about good code design, but I have a notion about your situation, and I'm posting it not so much because I think I'm right, as because I'd like to hear what everyone else has to say about it:
here goes..
If two modules load code from eachother, merge them into one module.
/me ducks head
| [reply] |
|
|
| [reply] |
|
|
I happened to be reading Big Ball of Mud, and I think it's good enough to mention the link. Ironically, the whole book is on one big page ;^)
| [reply] |
|
|
If two modules load code from eachother, merge them into one module.
In theory, yes. In practice; no, except maybe on a temporary basis; depending on how your mind works.
On a conceptual level, you need to discover how the two modules come together to form one or more logical units.
Then you need to take the code that comprises each one of those logical units, turn each logical unit into it's own distinct module.
Some people might find it's easier to put all the code together into one file while trying to sort out how the code works together; other people don't mind bouncing back and forth from file to file.
So, it's not a bad first step; but as chromatic points out, it shouldn't be your final step.
| [reply] |
Re: Advisability of circular dependencies in packages
by badaiaqrandista (Pilgrim) on Nov 14, 2006 at 00:41 UTC
|
I don't think it is possible. One module will be compiled before another and perl will throw a compile time error saying that subroutine such and such is not defined. I've burned with this one before, so don't do it.
Make display subroutines generic enough so they can display anything, regardless of where they come from. And make manipulation subroutines do not care how it will be displayed. Then make another module that uses subroutines from both modules.
| [reply] |