Presented at JavaZone (10th September 2014)
Video available at https://vimeo.com/105758303
But how much reason supports the rituals and mantras often repeated as coding guidelines? It turns out that the advice often fails, even for the novices they are intended to guide. Let's reason through these rather than accept them as unquestioned habits.
How many asserts should a test case have or not have? How much work should a constructor (not) do? What mantra guides test-first programming? How do you name your classes and other identifiers? How do you lay out your code? These questions and others have standard answers based on received and repeated mantras, practices that are communicated in good faith to be passed on as habits. But how much reason supports these assertions? It turns out that the advice often fails, even for the novices they are intended to guide.
This talk has little respect for ritual and tradition and takes no prisoners: What actually makes sense and what doesn't when it comes to matters of practice? What guidelines offer the greatest effect and the greatest learning?
3. I see Keep Out signs as suggestions more than actual orders.
Like Dry Clean Only.
4. Cargo cult programming is a style of computer programming characterized by the ritual inclusion of code or program structures that serve no real purpose.
Cargo cult programming can also refer to the results of applying a design pattern or coding style blindly without understanding the reasons behind that design principle.
http://en.wikipedia.org/wiki/Cargo_cult_programming
7. I currently have an average of 15-25 imports in each source file, which is seriously making my code mixed-up and confusing.
Is too many imports in your code a bad thing?
Is there any way around this?
http://stackoverflow.com/questions/8485689/too-many-imports-spamming-my-code
8. It's normal in Java world to have a lot of imports.
Not importing whole packages is good practice.
It's a good practice to import class by class instead of importing whole packages.
http://stackoverflow.com/questions/8485689/too-many-imports-spamming-my-code
10. It is not a problem. Any IDE will manage imports and show them to you only when needed.
Most IDEs support code folding where all the imports are folded down to one line. I rarely even see my imports these days as the IDE manages them and hides them as well.
Any good IDE, such as Eclipse, will collapse the imports in one line, and you can expand them when needed, so they won't clutter your view.
http://stackoverflow.com/questions/8485689/too-many-imports-spamming-my-code
11.
12. It is not a problem. Any IDE will manage imports and show them to you only when needed.
Most IDEs support code folding where all the imports are folded down to one line. I rarely even see my imports these days as the IDE manages them and hides them as well.
Any good IDE, such as Eclipse, will collapse the imports in one line, and you can expand them when needed, so they won't clutter your view.
http://stackoverflow.com/questions/8485689/too-many-imports-spamming-my-code
13. It is not a problem. Any IDE will manage imports and show them to you only when needed.
Most IDEs support code folding where all the imports are folded down to one line. I rarely even see my imports these days as the IDE manages them and hides them as well.
Any good IDE, such as Eclipse, will collapse the imports in one line, and you can expand them when needed, so they won't clutter your view.
http://stackoverflow.com/questions/8485689/too-many-imports-spamming-my-code
15. I currently have an average of 15-25 imports in each source file, which is seriously making my code mixed-up and confusing.
Is too many imports in your code a bad thing?
Is there any way around this?
http://stackoverflow.com/questions/8485689/too-many-imports-spamming-my-code
16.
17. Avoid Long Import Lists by Using Wildcards
Long lists of imports are daunting to the reader. We don't want to clutter up the tops of our modules with 80 lines of imports. Rather we want the imports to be a concise statement about which packages we collaborate with.
27. The principle stated that a good module structure should be both open and closed:
Closed, because clients need the module's services to proceed with their own development, and once they have settled on a version of the module should not be affected by the introduction of new services they do not need.
Open, because there is no guarantee that we will include right from the start every service potentially useful to some client.
28. [...] A good module structure should be [...] closed [...] because clients need the module's services to proceed with their own development, and once they have settled on a version of the module should not be affected by the introduction of new services they do not need.
29. Published Interface is a term I used (first in Refactoring) to refer to a class interface that's used outside the code base that it's defined in.
Martin Fowler http://martinfowler.com/bliki/PublishedInterface.html
30.
31. There is no problem changing a method name if you have access to all the code that calls that method. Even if the method is public, as long as you can reach and change all the callers, you can rename the method.
32. There is a problem only if the interface is being used by code that you cannot find and change. When this happens, I say that the interface becomes a published interface (a step beyond a public interface).
33. Published Interface is a term I used (first in Refactoring) to refer to a class interface that's used outside the code base that it's defined in.
The distinction between published and public is actually more important than that between public and private.
The reason is that with a non-published interface you can change it and update the calling code since it is all within a single code base. [...] But anything published so you can't reach the calling code needs more complicated treatment.
Martin Fowler http://martinfowler.com/bliki/PublishedInterface.html
34. [...] A good module structure should be [...] open [...] because there is no guarantee that we will include right from the start every service potentially useful to some client.
36. A myth in the object-oriented design community goes something like this: If you use object-oriented technology, you can take any class someone else wrote, and, by using it as a base class, refine it to do a similar task. Robert B Murray C++ Strategies and Tactics
37.
38. Design and implement for inheritance or else prohibit it
By now, it should be apparent that designing a class for inheritance places substantial limitations on the class.
39.
40. Bertrand Meyer gave us guidance as long ago as 1988 when he coined the now famous open-closed principle. To paraphrase him:
Software entites (classes, modules, functions, etc.) should be open for extension, but closed for modification.
Robert C Martin "The Open-Closed Principle" C++ Report, January 1996
42. http://blog.8thlight.com/uncle-bob/2014/05/12/TheOpenClosedPrinciple.html
I've heard it said that the OCP is wrong, unworkable, impractical, and not for real programmers with real work to do. The rise of plugin architectures makes it plain that these views are utter nonsense. On the contrary, a strong plugin architecture is likely to be the most important aspect of future software systems.
43. public abstract class Shape
{
...
}
public class Square extends Shape
{
...
public void drawSquare() ...
}
public class Circle extends Shape
{
...
public void drawCircle() ...
}
44. public abstract class Shape ...
public class Square extends Shape ...
public class Circle extends Shape ...
static void drawAllShapes(Shape[] list) { for(Shape s : list) if(s instanceof Square) ((Square) s).drawSquare(); else if(s instanceof Circle) ((Circle) s).drawCircle(); }
45. public abstract class Shape ...
public class Square extends Shape ...
public class Circle extends Shape ...
static void drawAllShapes(Shape[] list)
{
for(Shape s : list)
if(s instanceof Square)
((Square) s).drawSquare();
else if(s instanceof Circle)
((Circle) s).drawCircle();
}
46. public abstract class Shape
{
...
public abstract void draw();
}
public class Square extends Shape
{
...
public void draw() ...
}
public class Circle extends Shape
{
...
public void draw() ...
}
47. public abstract class Shape ...
public class Square extends Shape ...
public class Circle extends Shape ...
static void drawAllShapes(Shape[] list)
{
for(Shape s : list)
s.draw();
}
48. public abstract class Shape ...
public class Square extends Shape ...
public class Circle extends Shape ...
static void drawAllShapes(Shape[] list) { for(Shape s : list) s.draw(); }
49. public abstract class Shape ...
public class Square extends Shape ...
public class Circle extends Shape ...
static void drawAllShapes(Shape[] list)
{
for(Shape s : list)
s.draw();
}
50. Bertrand Meyer gave us guidance as long ago as 1988 when he coined the now famous open-closed principle. To paraphrase him:
Software entites (classes, modules, functions, etc.) should be open for extension, but closed for modification.
Robert C Martin "The Open-Closed Principle" C++ Report, January 1996
51. Bertrand Meyer gave us guidance as long ago as 1988 when he coined the now famous open-closed principle. To paraphrase him:
Software entites (classes, modules, functions, etc.) should be open for extension, but closed for modification.
Robert C Martin "The Open-Closed Principle" C++ Report, January 1996
52. This double requirement looks like a dilemma, and classical module structures offer no clue.
But inheritance solves it.
A class is closed, since it may be compiled, stored in a library, baselined, and used by client classes. But it is also open, since any new class may use it as parent, adding new features.
53. public abstract class Shape ...
public class Square extends Shape ...
public class Circle extends Shape ...
static void drawAllShapes(Shape[] list)
{
for(Shape s : list)
if(s instanceof Square)
((Square) s).drawSquare();
else if(s instanceof Circle)
((Circle) s).drawCircle();
}
54. public abstract class Shape ...
public class Square extends Shape ...
public class Circle extends Shape ...
static void drawAllShapes(Shape[] list)
{
for(Shape s : list)
if(s instanceof Square)
((Square) s).drawSquare();
else if(s instanceof Circle)
((Circle) s).drawCircle();
}
55. public abstract class Shape ...
public class Square extends Shape ...
public class Circle extends Shape ...
static void drawAllShapes(Shape[] list) { for(Shape s : list) s.draw(); }
56. public abstract class Shape ...
public class Square extends Shape ...
public class Circle extends Shape ...
static void drawAllShapes(Shape[] list)
{
for(Shape s : list)
s.draw();
}
57. Don't publish interfaces prematurely.
Modify your code ownership policies to smooth refactoring.
62. Immutable Value The internal state of a value object is set at construction and no subsequent modifications are allowed.
63. To some, the most important aspect of exceptions is that they provide a general mechanism for reporting errors detected in a constructor.
Bjarne Stroustrup The Design and Evolution of C++
87. Thus only indicative sentences which it makes sense to think of as being true or as being false are capable of expressing propositions.
88. Make definite assertions. Avoid tame, colourless, hesitating, noncommittal language.
Note [...] that when a sentence is made stronger, it usually becomes shorter. Thus brevity is a by-product of vigour.
William Strunk and E B White The Elements of Style
92. One of the things that Osherove warns against is multiple asserts in unit tests.
Owen Pellegrin http://www.owenpellegrin.com/blog/testing/how-do-you-solve-multiple-asserts/
93. Proper unit tests should fail for exactly one reason, that’s why you should be using one assert per unit test.
http://rauchy.net/oapt/
100. My guideline is usually that you test one logical concept per test. You can have multiple asserts on the same object. They will usually be the same concept being tested.
Roy Osherove http://www.owenpellegrin.com/blog/testing/how-do-you-solve-multiple-asserts/
101. One of the most foundational principles of good design is:
Gather together those things that change for the same reason, and separate those things that change for different reasons.
This principle is often known as the single responsibility principle, or SRP. In short, it says that a subsystem, module, class, or even a function, should not have more than one reason to change.
102. A test case should be just that: it should correspond to a single case.