Yes I totally get what you are saying. At one point I might not have written the last unix_time() example, but that is my default now, UNTIL say I hit a bug and need to test it thoroughly.
GetHomeDir() is a great example that proves that point... if I were trying to be very pure, I would have reified the pwd dependency and environment dependency.
One reason to avoid the complication is that I'm using shell scripts for tests. So in shell you already have a test environment. You can set $HOME to whatever, and in theory /etc/passwd, although that's a little trickier.
Actually that was one of my motivations for writing a shell :) You can test "one level out" at the OS level. Instead of the "seam" being the language, the seam is the OS.
I prefer to test against STABLE INTERFACES, not against things that I made up internally. You don't want your tests to calcify the structure of your code. I've seen that happen a lot with fine-grained testing, and it's a big pitfall.
I would say at the beginning, I write unit tests for tricky parts, but don't aim for 100% unit test coverage. I aim more for high integration test coverage. And then at the end of the project, when you are fixing bugs, that is when you can do fine-grained testing without worrying about making a mess of your code.
Here I did parameterize random() (irr_rand), because it's very important to the function of the code and needs to be tested:
So it all depends on the context. That's why I say it takes some practice. It takes practice to:
- not end up with more than 3-4 parameters for each class.
- not end up with too many classes. Java code seems to fall into this. A lot of things are just functions with dependencies. Actually this style I think reduces the need for classes -- they are your parameters rather than being your context!
- not structuring your code as a deep tree of calls. Instead it should be a relatively flat object graph.
A relatively static "Object graph" is really the idea that distinguishes OOP from "structured programming" (i.e. a pyramid).
I think I would differ with you in that I don't think DI and functional programming are that different. I think they are trying to get at the same core idea.
Things I ALMOST NEVER use in Python:
- setters and getters.
- Especially, setters for dependency injection. I always pass params through constructors.
- decorators. This can always be accomplished with composition of objects. I find that style a lot more readable. decorators are non-trivial code at the global level, when it really should be in main().
- classmethod and staticmethod. Static methods should just be functions. Class method is kind of a hack for singleton-like behavior.
- As mentioned, the singleton pattern is banned. Singletons are just classes you instantiate once.
I like that this style gets rid of a lot of concepts: thread local (as mentioned above), explicit singleton pattern, and "static/class methods".
Also I think it's true that Java's static type system might get in the way a little bit, but I don't have a strong conclusion on that. However, I also wish Python were a little more strict. I don't use 90% of the dynamism of classes.
GetHomeDir() is a great example that proves that point... if I were trying to be very pure, I would have reified the pwd dependency and environment dependency.
One reason to avoid the complication is that I'm using shell scripts for tests. So in shell you already have a test environment. You can set $HOME to whatever, and in theory /etc/passwd, although that's a little trickier.
Actually that was one of my motivations for writing a shell :) You can test "one level out" at the OS level. Instead of the "seam" being the language, the seam is the OS.
I prefer to test against STABLE INTERFACES, not against things that I made up internally. You don't want your tests to calcify the structure of your code. I've seen that happen a lot with fine-grained testing, and it's a big pitfall.
I would say at the beginning, I write unit tests for tricky parts, but don't aim for 100% unit test coverage. I aim more for high integration test coverage. And then at the end of the project, when you are fixing bugs, that is when you can do fine-grained testing without worrying about making a mess of your code.
Here I did parameterize random() (irr_rand), because it's very important to the function of the code and needs to be tested:
https://github.com/google/rappor/blob/master/client/python/r...
So it all depends on the context. That's why I say it takes some practice. It takes practice to:
- not end up with more than 3-4 parameters for each class.
- not end up with too many classes. Java code seems to fall into this. A lot of things are just functions with dependencies. Actually this style I think reduces the need for classes -- they are your parameters rather than being your context!
- not structuring your code as a deep tree of calls. Instead it should be a relatively flat object graph.
A relatively static "Object graph" is really the idea that distinguishes OOP from "structured programming" (i.e. a pyramid).
I think I would differ with you in that I don't think DI and functional programming are that different. I think they are trying to get at the same core idea.
Things I ALMOST NEVER use in Python:
- setters and getters.
- Especially, setters for dependency injection. I always pass params through constructors.
- decorators. This can always be accomplished with composition of objects. I find that style a lot more readable. decorators are non-trivial code at the global level, when it really should be in main().
- classmethod and staticmethod. Static methods should just be functions. Class method is kind of a hack for singleton-like behavior.
- As mentioned, the singleton pattern is banned. Singletons are just classes you instantiate once.
I like that this style gets rid of a lot of concepts: thread local (as mentioned above), explicit singleton pattern, and "static/class methods".
Also I think it's true that Java's static type system might get in the way a little bit, but I don't have a strong conclusion on that. However, I also wish Python were a little more strict. I don't use 90% of the dynamism of classes.