Can you have too much of “dynamic” in dynamic languages?
In last few months I have been making a transition from Java to Groovy and I can appreciate many of the benefits it brings: less code, closures, builders, MOP that in the end makes framework like Grails possible, ease with mocking when writing tests etc.
However, I have been “accused” by my coworkers that my code is not groovy enough. Namely, I still declare types for my parameters and fields, tend to use inheritance and polymorphism instead of duck typing etc. It seems to me that in these situations it is not only dynamic vs. static, but also dynamic vs. object-oriented paradigm kind of dilemma. In those cases I still tend to prefer OO. I feel that OO paradigm has great value in its basic premise in allowing you to abstract and relate your code constructs to particu开发者_开发技巧lar real-world concepts.
So, here are particular questions I need help with:
Should I declare types for my parameters, fields, etc?
Should I declare block of code as closure when simple method will do?
When should I use duck typing instead of polymorphic dynamic dispatch. For example, in groovy I can do animal."$action"() or def animal; animal.action() , instead of Animal animal = new Dog(); animal.action(). I can see the problem with first in the context of Open-Closed principle, but any other reasons to prefer OO style polymorphism?
When should I use interfaces in groovy (if ever)?
I am sure that there are some other similar dilemmas I failed to write down. I also think that these questions are valid not just for groovy, but for any other dynamic language. What is your opinion?
This isn't always a popular opinion, but I think that the more explicit and clear your code can be, the better.
I dislike constructs that leave you guessing just what's going on...
I worked for a year in Ruby and didn't like it at all. I'm not saying that it doesn't have places where it excels, I'm just saying that I really like to keep things clean and explicit and didn't feel that Ruby had that as a goal.
One thing I did figure out for sure--the amount of typing you do does not equate to overall development speed. It's true that a complicated code base with a lot of repeated code makes for very slow development, but simply reducing what you type without eliminating the duplication is useless, and typing longer, clearer, more explicit code is going to generally be faster (over the length of the project) than the same code written in a terse, less formal language.
If you don't believe that typing has no relation to development speed, next time you release a project count the number of lines of code and divide by the man-days spent (including debugging and testing). In other words, how much code was typed a day. You'll find the result to be a very small number--actually typing code is a very small part of any software project.
I think with Groovy you should favor the simplest way of doing something, and fall back on the groovier features only when the situation calls for it. (Like when writing macros or creating multimethods in Clojure, if you find yourself reaching for those tools a lot you should question yourself.) Your cautious approach seems fine to me, possibly your co-workers are a little intoxicated with their new-found power. (It wouldn't be the first time.)
The good thing about having a flexible language like Groovy is you can start off with the cautious approach like you favor knowing you have more powerful alternatives to fall back on when you need them. You know, "the simplest thing that could possibly work."
More specifically preferring duck typing over interfaces and not bothering with types on parameters seems like it could be a good thing, it may make it a lot easier to supply mocks to tests.
.1. Should I declare types for my parameters, fields, etc?
I tend declare types on classes that are used as part of a public API, things that other developers will be consuming a lot, or where I'd like some additional autocomplete help from IntelliJ. Otherwise I 'def' things.
.2. Should I declare block of code as closure when simple method will do?
I use methods unless it's something I plan on passing around as a variable. Even though there is the "foo.&bar" method dereference operator, most devs don't know about this and are confused by it when they run across it. I also use closures when it's a small piece of code that is clear to remain in a larger method rather than put in it's own method for descriptive purposes.
.3. When should I use duck typing instead of polymorphic dynamic dispatch. For example, in groovy I can do animal."$action"() or def animal; animal.action() , instead of Animal animal = new Dog(); animal.action(). I can see the problem with first in the context of Open-Closed principle, but any other reasons to prefer OO style polymorphism?
I only use the animal."$action"() form when I need that level of indirection because the method name varies based on the code execution path (most often during heavy metaprogramming). I use Animal animal = new Dog(); animal.action() when I want the IDE's help with autocompletion, or that level of documentation is helpful for code clarity (and not hurt by the extra verboseness that might be obvious or constraining).
.4. When should I use interfaces in groovy (if ever)?
I very rarely use them. I could see using them mostly as documentation of the expected fields for a public API call, or possibly as a marker interface to help distinguish one set of classes from another from a metaprogramming perspective. They're much less useful in groovy than they are in java.
It comes down to what people are comfortable with. I like to use type declerations and method calls because I'm comfortable in Java. The code I write has to be maintained by people with out much dynamic programming experience so I keep it close to normal Java code unless there's a good reason to use an advanced groovy feature. IT sounds like your group should create coding standards that try to explain when specific features of Groovy should generally be used.
There are two dominant types of object-oriented languages.
The languages in the Simula 67 family, such as C++ and Java, favor statically-typed variables, compilers and linkers, and method vtables.
The languages in the Smalltalk family, such as Ruby, favor dynamically-typed variables, interpretation, and message-passing instead of function-pointer-tables.
Both are object-oriented, but very different takes on the concept of object-orientation.
YES
--
NO
Do you realize there is no real answer for this question?
精彩评论