*sigh* here we go again. OK, let me preface my response by saying that I think we're really
bikeshedding here. At the end of the day, if you know what you're doing, use whatever works for you. Having said that..
elija wrote:setrofim wrote:Basically, you would just be creating more work for yourself. You don't really gain anything by
creating a multude of files, each with only a few lines in it. The habit of sticking each class into its own file (and multi-level namespacing into different directories) is one of the reasons why it's a nightmare to write Java without an IDE. In Python a single .py file double up as a module and comes with it's own namespace. If you put each class into its own file, your code would be full of "from my_class import MyClass" kind of lines.
(From
here but it seems more appropriate to continue here)
I would say that the extra work is a worthwhile trade off for code readability and maintainability
Quite the opposite. When I said you're creating more work for yourself, I meant going forward, i.e. in terms of maintainability. It means that in order to do pretty much anything--debug defects, add features, refactor, whatever--you have to deal with, and keep track of, a bazillion files. You'll have a ton of imports all over the place, and will have to keep shuffling those. You're increasing the chances that you'll miss a file in your commit and so end up pushing a breaking change. You're duplicating information, since the same thing is encoded in the name of the module and the name of the class -- you're making your namespaces deeper without adding anything semantically (this is not an issue in languages like Java, since there files do not have namespaces), and when you rename a class, you have to remember to rename the file as well (easy to miss this when you're using a refactoring tool to rename the class at the place of instantiation). Sure none of these are "major" issues and they can be mitigated buy using a decent IDE; but why create the hassle in the first place? How is putting every single thing into its own file going to help with maintainability? Splitting your program out into a few logically coherent modules is enough.
I don't believe readability really comes into this. How is code split into several files more readable than the same code in one file (appropriately commented and laid out)? Splitting things that are closely related to each other but are loosely coupled to the rest of the code into a separate file makes sense, and I'm certainly not advocating keeping everything in a single file (though that has its advantages, e.g.
bottle micro framework is purposefully staying a single-file library). But splitting each class into it's own file for the hell of it is going too far and does not make things "more readable", only more annoying as you have to keep jumping between files.
elija wrote:I take it that there is no equivalent to PHP's __autoload function in Python?
Not really, no (at least if I understood what __autoload does correctly from the docs -- I don't know PHP). In Python, you have to explicitly load modules before you can use them. There is the "from module import *" notation which imports the contents of the module into the current namespace without having to explicitly list them, but it's not the same thing (and the use of this notation is very much discouraged).
"Explicit is better than implicit." and all that.
elija wrote:In any case writing loosely coupled code is, while sometimes more tricky, usually a way to better and more testable code.
That's true, up to a point. Trying to make the coupling as loose as possible results in horrendously over-abstracted code that is impossible to comprehend (let alone maintain or test). People end up writing plugin frameworks that only ever going to load one plugin, overly verbose APIs, enums with only one value (but hey, we might add more later). One of the advantages of dynamic typing is that you can write future-proof and flexible code without over-engineering things.
elija wrote:Consider the following
- Code: Select all
class SomeClass(object):
... snip ...
def someMethod(self, i):
anotherObject = ADifferentObject(i)
## Do stuff in the method ##
return anotherObject.someMethod(aParameter)
... snip ...
when compared with
- Code: Select all
class SomeClass(object):
... snip ...
def someMethod(self, anotherObject):
## Do stuff in the method ##
return anotherObject.someMethod(aParameter)
... snip ...
In the second example, SomeClass and anotherObject are loosely coupled which allows us to unit test the behaviour of someMethod without needing a full implentation of anotherObject. Instead we can pass in a
mock object with just enough of it's behaviour implemented for our test. The design pattern used in the second example is called
Dependency Injection.
I'm not sure how this relates to our discussion? Dependency injection does not necessitate putting classes into separate files (and the act of moving a class into it's own files does
not somehow loosen its coupling -- that is determined by what the class depends on and what depends on it).