tag:blogger.com,1999:blog-75079832472488380782024-03-13T09:44:45.027-07:00Pondering Object Oriented DesignHopefully some insight into object oriented programming.
<a href="http://technorati.com/faves?sub=addfavbtn&add=http://curtiscooley.com"><img src="http://static.technorati.com/pix/fave/btn-fave2.png" alt="Add to Technorati Favorites"></a>Curtis Cooleyhttp://www.blogger.com/profile/13559671537311158727noreply@blogger.comBlogger21125tag:blogger.com,1999:blog-7507983247248838078.post-695240024156349572019-03-29T07:46:00.000-07:002019-03-29T07:46:35.475-07:00Code Deploy Pre and Post Hooks in JavaThe Serverless Framework <a href="https://serverless.com/blog/manage-canary-deployments-lambda-functions-serverless-framework">blog</a> has a nice post on how to do blue/green or <a href="https://martinfowler.com/bliki/CanaryRelease.html">canary deploys.</a> Most of the work is done in plugins and in the serverless.yml file, but for safe automated canary deploys, you'll want to add pre and post hooks.<br />
<br />
As you might infer from their names, the pre-hook runs before the deployment and the post-hook runs once the canary deployment is complete.<br />
<br />
If you return a "Failure" from either hook, Code Deploy will automatically rollback your deployment.<br />
<br />
The missing piece from the mentioned blog is writing a hook in Java. The only example is in Node.js.<br />
<br />
I experimented for a day and now share my results. This should be enough to get you started writing AWS Code Deploy hooks in Java.<br />
<br />
<pre>public class PreHook implements RequestHandler<Map<String, String>, PutLifecycleEventHookExecutionStatusResult> {
@Override
public PutLifecycleEventHookExecutionStatusResult handleRequest(Map<string, string> event, Context context) {
String deploymentId = event.get("DeploymentId");
String lifecycleEventHookExecutionId = event.get("LifecycleEventHookExecutionId");
PutLifecycleEventHookExecutionStatusRequest request = new PutLifecycleEventHookExecutionStatusRequest();
request.setDeploymentId(deploymentId);
request.setLifecycleEventHookExecutionId(lifecycleEventHookExecutionId);
// Do your integration tests here. If they pass, set the status to "Succeeded" otherwise "Failed"
// I set it to "Failed" to test rollback
request.setStatus("Failed");
AmazonCodeDeploy codeDeploy = AmazonCodeDeployClientBuilder.defaultClient();
return codeDeploy.putLifecycleEventHookExecutionStatus(request);
}
</pre>
<br />
Like most Java Lambdas, you extend <span style="font-family: "courier new" , "courier" , monospace;">RequestHandler</span> and parameterize it.<br />
<br />
The input parameter is a <span style="font-family: "courier new" , "courier" , monospace;">Map</span> of <span style="font-family: "courier new" , "courier" , monospace;">Strings</span>. The map contains two very important pieces of information. The <span style="font-family: "courier new" , "courier" , monospace;">DeploymentId</span> and the <span style="font-family: "courier new" , "courier" , monospace;">LifeCycleEventHookExecutionId</span>.<br />
<br />
Both are required to create a <span style="font-family: "courier new" , "courier" , monospace;">PuLifecycleEventHookExecutionStatusRequest</span>.<br />
<br />
<span style="font-family: "courier new", courier, monospace;">PuLifecycleEventHookExecutionStatusRequest </span><span style="font-family: Times, Times New Roman, serif;">is required to create the output parameter </span><span style="font-family: Courier New, Courier, monospace;">PutLifecycleEventHookExecutionStatusResult</span> by way of a <span style="font-family: Courier New, Courier, monospace;">AmazonCodeDeploy</span> object.<br />
<br />
Hopefully the code is enough to get you started. If not, please leave a comment or come find <a href="https://forum.serverless.com/u/DarkSavant/activity">TheDarkSavant</a> at the <a href="https://forum.serverless.com/latest">Serverless forums</a>.<br />
<br />
<br />Curtis Cooleyhttp://www.blogger.com/profile/13559671537311158727noreply@blogger.com0tag:blogger.com,1999:blog-7507983247248838078.post-56732133319301272402011-02-25T15:10:00.000-08:002011-02-25T16:23:28.671-08:00Parameterized TypesI recently had a major epiphany. I always thought Java Generics was about type safety until I realized that they are an implementation of the more general term "parameterized types" and thus are about reuse. Basically, in object oriented programming there are three ways to reuse code: inheritance, composition, and parameterized types. Let's look at how to use parameterized types for reuse.<br />
<br />
Let's use the pedantic example of sorting. You've written a new ultra fast O(n) sorting algorithm, and you want to use it to sort all kinds of things, not just integers. In the pre Java 5 world, your sorting class would also implement something like Comparable, so any implementing class would have to override compareTo() for the actual comparison. That's the inheritance way to reuse. But that means your sorting class violate SRP. Now it sorts AND compares. You could have the implementor pass in a Comparator at construction and delegate to it during sorting, that's the composition way to reuse, but you'd rather not have a third object involved for clarity. What you can do is parameterize your sorting algorithm so the type of object that is getting sorted must implement Comparable, and let the sorted object itself decide how to compare itself to other objects of the same type.<br />
<br />
<pre>public class SuperCoolSorter<T extends Comparable<T>> {
public T[] sort(T[] collection) {
for (T t : collection) {
// secret O(n) sorting
T other = t;
t.compareTo(other);
}
return collection;
}
}
</pre><br />
So when you want to sort BugBears:<br />
Create a BugBear class that implements Comparable<BugBear><br />
<pre>public class BugBear implements Comparable<BugBear> {
public int compareTo(BugBear other) {
// your comparison
}
}
</pre><br />
then new up sorter parameterizing it with BugBears:<br />
<br />
<pre>SuperCoolSorter<BugBear> bugBearSorter = new SuperCoolSorter<BugBear>>();
bugBearSorter.sort(// bugbear array)
</pre><br />
You've now created a sorting class that is independent of how things it sorts get compared, yet is reusable to sort anything that implements Comparable. Of course you could just have sort take in an array of Comparable, but then you'd have to return an array of Comparable then force the client to cast. By using parameterized types, you also control which type gets returned from sort.<br />
<br />
Although this is pretty Java Generics specific, it's a usable pattern for any language that implements parameterized types. For example, C++ using templates.<br />
<br />
Also, loosely typed languages like Ruby, Groovy, and Smalltalk do not need the extra markup to implement this pattern. Since they are "duck typed" languages, you can just call compareTo() on the passed in objects without the all the extra ceremony.<br />
<br />
There you have it. The third type of reuse you may have never known about.Curtis Cooleyhttp://www.blogger.com/profile/13559671537311158727noreply@blogger.com1tag:blogger.com,1999:blog-7507983247248838078.post-44993132526596704672010-02-18T15:21:00.000-08:002010-02-18T15:34:06.900-08:00Code Katas I'd Like to See More OfIf you haven't heard of code katas, then you should really check them out. Like their namesake in Karate, code katas are little pieces of code you write over and over to exercise your coding muscles. And as opposed to bench pressing to exercise your coding muscles, katas are designed to fine tune not just strengthen.<br />
<br />
I'm not sure who coined the term or idea, but I tend to get most of my katas from Dave Thomas at http://codekata.pragprog.com/<br />
<br />
I like the katas listed there, but my experience is that they are mostly just programming contest problems designed to exercise your problem solving skills. That's a good thing, by the way, but there are more than just problem solving skills to master on the road to becoming a software craftsman.<br />
<br />
What I'd like to see is more katas like:<br />
<br />
Given the follow class:<br />
<br />
<pre>import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import junit.framework.TestCase;
public class PolyKata extends TestCase {
private PrintStream stream;
enum shape {square, triangle, rectangle}
private void drawPoly(shape the_shape) {
switch (the_shape) {
case square:
stream.print(shape.square);
break;
case triangle:
stream.print(shape.triangle);
break;
case rectangle:
stream.print(shape.rectangle);
break;
}
}
public void test_drawPoly_square() throws Exception {
PrintStreamFake stream_fake = new PrintStreamFake(shape.square);
stream = stream_fake;
drawPoly(shape.square);
}
public void test_drawPoly_triangle() throws Exception {
PrintStreamFake stream_fake = new PrintStreamFake(shape.triangle);
stream = stream_fake;
drawPoly(shape.triangle);
}
public void test_drawPoly_rectangle() throws Exception {
PrintStreamFake stream_fake = new PrintStreamFake(shape.rectangle);
stream = stream_fake;
drawPoly(shape.rectangle);
}
private static final class PrintStreamFake extends PrintStream {
private shape the_shape;
public PrintStreamFake(shape theShape) throws IOException {
super(File.createTempFile("temp", "file"));
this.the_shape = theShape;
}
@Override
public void print(Object obj) {
assertEquals(obj, the_shape);
}
}
}
</pre><br />
<br />
Refactor using "replace conditional with polymorphism". Do this kata five days in a row, refactoring to a different design pattern each time. Then do it one more time only this time focus on taking the smallest steps possible and still keep the tests green. Also find ways to clean up the test code, as there's some duplication that can go as well.<br />
<br />
To become a craftsman, you must refine your problem solving and coding skills, but you must also refine your code nose and refactoring skills. You must learn to quickly identify the stench of crap code and perfect your refactoring skills so you can quickly, cleanly, and efficiently replace crap code with clean code.<br />
<br />
This is a perfect task for code katas. I only hope more of these come into play. I'll try to contribute my fair share, and if there are katas of this ilk out there in the tubes of the Internets already, please don't hesitate to point them out in the comments.<br />
<br />
And I have to add, I had to look up how to do enums and switch statements in Java. That's how much I use them :)Curtis Cooleyhttp://www.blogger.com/profile/13559671537311158727noreply@blogger.com0tag:blogger.com,1999:blog-7507983247248838078.post-35971488321425407502010-02-09T14:04:00.000-08:002010-02-09T14:04:15.987-08:00Don't Swallow Exceptions - Ever!Among lots and lots of good reason not to swallow exceptions, I just ran into a doozy. I'm rewriting a VB.Net application in Java. It has some really complex calculations. One of the calculations involves using a 2D array to setup some default values. The array is in a try catch block. With a catch(Exception e) and nothing else. No logging. Nothing.<br />
<br />
Two days of trying to figure out why my calculations are slightly off what's expected. Thank you FitNesse, by the way. Anyway, come to realize that I'm swallowing an IndexOutOfBoundsException. See, VB.Net starts arrays at 1 while Java starts arrays at 0, so my array initialization was barfing and returning a bad value.<br />
<br />
I did what most of us do now and then. I saw the empty catch block and thought, "I'll get back to that. I just want to get these calculations right. Then I'll fix those exceptions." Woops.<br />
<br />
Handle exceptions as soon as you write the catch block. Handle it as best you can. At the very least print it to standard error, so when it happens you know it happened. Had I done at least that, I wouldn't be two more days behind.Curtis Cooleyhttp://www.blogger.com/profile/13559671537311158727noreply@blogger.com2tag:blogger.com,1999:blog-7507983247248838078.post-60375555130864276902010-01-21T10:24:00.000-08:002010-01-21T10:24:32.744-08:00XP is Not a Set of PracticesA recent discussion on the XP Yahoo! group has gotten me thinking about Extreme Programming and Agile Software Development in general. The discussion is around to test or not to test. Kent Beck's blog post provides an over all look into his view at <a href="http://www.threeriversinstitute.org/blog/?p=187">To Test or Not To Test</a><br />
<br />
The discussion started with someone asking for XP Myths and to test or not to test popped up and hijacked the thread. If you are really interested in the gory details of the thread, see <b>http://tinyurl.com/yes8b2u </b>for details. The discussion then stemmed into something like, "well, if it's OK to not test when you are exploring what people will pay for, it might be OK to not do XP". But what does "not do XP" mean?<br />
<br />
That got me thinking about "Lean Thinking" by Womak and Jones because I've never looked at XP as a set of practices, well, I did early on but grew out of it. I look at XP and any Agile methodology as a system with certain core values and principles. I prefer to build my XP process based on the same principles expressed in "Lean Thinking:" value, value stream, flow, and perfection. If you want a more detailed list of principles on which to base your agile methodology, see "Lean Software Development" which has, if I recall correctly, 7.<br />
<h2>Value</h2>From a lean perspective, value is what you sell to make money. In software that means, well, software. It's why the customer pays you. The customer pays you because the software saves or makes her money. So the main principle is everything you do in your process must create value or it's <i>muda</i> or 'waste'. Whenever you decide to implement a practice ask yourself, "Will this practice help the team create value?" Alternately, whenever you decide to skip a practice, ask yourself, "How will skipping this practice affect the teams ability to create value?"<br />
<h2>Value Stream</h2>The value stream is the entire production process. Often it is helpful to map your value stream to see how the value gets produced. In manufacturing it could start at the mine where the raw materials are extracted from the Earth and end where the shiny new product rolls off the assembly line.<br />
<br />
In software, your value stream could start with the customer asking for a new feature and flow all the way to delivery back to the customer. To map your value stream, write down every step the team takes while delivering that new feature. Once you have a complete value stream map, you can start looking for muda. Things you do that do not directly contribute to the creation of the desired feature - the <i>value</i>. Also look at all activities the team does that are not in the value stream map. Those activities are <i>primary muda</i>. <i>Secondary muda</i> are activities that contribute in a secondary way but do not directly produce value. An example of secondary muda is source control activities. Using a source control system is a vital component to software development. It keeps track of file changes, enables sharing of code, and allows branching for tracking software versions. All key to developing good software, but none add directly to the value. In other words, a customer will not pay you more money whether you use an SCM or not. In secondary muda cases, the goal is to minimize the time and effort spent on the activity. If the team spends 2 hours a day mucking about with SCM activities, find out why and reduce that time, so the team can spend more time on valuable activities.<br />
<h2>Flow</h2>The current buzzword of the agile industry. In manufacturing, flow is how well the value moves through the value stream. It addresses things like inventory and production backlog. For example, if a certain station on the assembly line always has a huge inventory of product stacked up in front of it, and the station down line is always waiting, then there is a flow issue. This is where the Toyota Production System concept of <i>pull</i> comes in to play. Traditional assembly lines thought the plant was most efficient when run at full capacity, but that often produces flow problems. Pull allows each station to pull product from the previous station, so each station can work at its full capacity. Less waste.<br />
<br />
The current Kanban movement in software development is based on the concepts of flow and pull. The swimlanes on a Kanban board often map to the value stream map with each swimlane able to pull from the previous swimlane. All of your activities should allow the value to flow through the value stream. Once you have your value stream map, you analyze how the value flows through the system and address bottlenecks.<br />
<h2>Perfection</h2>Perfection addresses how the Toyota Production System constantly strove to improve itself. <i>Kaizen</i> is the constant little improvements each team tries to do everyday thinking that small continuous improvements eventually add up to big gains in effectiveness.<br />
<br />
In software, retrospectives are often used to identify and address any issues that came up during the previous iteration or sprint. Encourage your team to constantly identify any issues with flow. As the team gets better and better, different problems pop up. Perhaps the team as become very proficient with the new technology and is producing faster than test can verify. Certainly a good problem to have, but a flow problem none the less. According to Theory of Constraints thinking, you move the constraining part ahead of the part that's overwhelming it. Moving test ahead of development, writing automated tests that are executed as the release criteria, now frees test from getting swamped and sets valuable limits on development.<br />
<h2>Conclusion</h2>I'm not willing to say what XP is, but I am willing to say it is not just a set of practices. I do believe the best methodologies are built from principles and values and not from a list of practices which are dogmatically executed day in and day out. Each practice you decide to adopt, be it a traditional XP practice, one from another methodology, or one the team creates itself, should contribute to value, value stream, flow, or perfection. Only then will you know it is worth trying. When evaluating whether to dump or skip a practice, analyze how that practice contributes to value, value stream, flow, or perfection to determine the affect dropping the practice will have.Curtis Cooleyhttp://www.blogger.com/profile/13559671537311158727noreply@blogger.com0tag:blogger.com,1999:blog-7507983247248838078.post-13859108949056426512009-09-04T16:59:00.000-07:002009-09-04T17:23:57.850-07:00Debt Metaphor is not Technical DebtWard Cunningham's coined phrase "<a href="http://www.youtube.com/watch?v=pqeJFYwnkjE">the debt metaphor</a>" has caused some confusion. I've seen it mis phrased as "technical debt." This is a mistake because if you watch the linked video, you'll see that Ward is talking about your understanding of the features and the model, and not about going into debt by taking programming short cuts.<br /><br />Let's take a look at how Extreme Programming does design. Kent Beck expresses the XP practices as taking the good things about development and turning the knob to eleven. This includes design. If design is good, don't relegate it to a phase of development then end that phase and start construction, i.e. big design upfront (BDUF). Do it all the time and allow the design to emerge. This is where the terms emergent design and evolutionary design began.<br /><br />But if you are going to start writing code on day one, and only do enough design to complete the current story, then you are writing code with a smaller understanding of the problem than you would had you done BDUF. So you are going into design debt in order to allow the design to emerge. As your understanding grows, you can refactor the code to better reflect your new understanding. That's paying off the debt. For that to work, the code needs to be as clean as possible.<br /><br />I see blogs and email posts talking about technical debt and tracking technical debt so you can pay it back. That's OK that they want to talk about technical debt, but sometimes I see them using Ward's debt metaphor as validation for this practice. Taking shortcuts, like not pairing, not doing TDD, not automating your acceptance tests, or just plane not writing clean code or refactoring to clean code will actually hamper your ability to pay back your design debt because now your code does not express the knowledge you have about the system as well as it should. You are cheating yourself in two ways. You are creating technical debt that the team will have to pay back in the form of refactoring or, more often, bug fixes, and you are severely hampering your ability to pay back the design debt because the code does not clearly express current design knowledge.<br /><br />What's the point? First, don't take shortcuts. Be honest and firm with your customer and tell them that you are sorry, but she can only have 2 of the 3 must have features for the upcoming trade show. And second, if you think it's OK to go into technical debt to please your customer, don't think that is what the debt metaphor enables. For the debt metaphor to work, you absolutely can not go into technical debt. Period.Curtis Cooleyhttp://www.blogger.com/profile/13559671537311158727noreply@blogger.com1tag:blogger.com,1999:blog-7507983247248838078.post-38695763039115482702009-09-04T14:24:00.001-07:002009-09-04T15:23:25.638-07:00Open Letter to Yahoo!This is off topic for this blog, but I need to get it off my chest. I'll try to be civil. At least more civil than <a href="http://www.youtube.com/watch?v=U98U5-zhjO0">LaGarrette Blount</a>.<br /><br />Dear Yahoo!<br /><br />I'm kicking you to the curb. I just can't take it anymore. Here's something I know you won't be able to believe, I'm writing this blog using Firefox on a machine running Ubunut 8.04. Really. Blogger.com actually runs in Firefox on Linux. Cool huh? I don't know how they do either. Well, actually I do 'cause I'm not a Steve Balmer boot boy.<br /><br />That's right, I'm tired of seeing "This Yahoo! application doesn't work on your browser/OS." Do you know what a 'web application' is? Do you? It's an application that runs on the web, not just on IE in Windows. But if you took any time away from sucking up to Microsoft trying to get them to buy your sorry ass to learn anything about the world wide web, HTML, or standards, you'd already know that.<br /><br />So go ahead and pander to the Microsoft world. Here's some news, <a href="http://www.techradar.com/news/internet/firefox-3-0-is-europe-s-most-popular-browser-588938">Firefox is the most popular browser in Europe</a>. But what do you care. Europe's only got like 830.4 million people. I'm sure you don't need any of that revenue to maintain your distant second place to Google in search engine revenue.<br /><br />And here's another little piece of news. YOU ARE THE ONLY FANTASY FOOTBALL SITE THAT CHARGES FOR LIVE UPDATES. CBS Sportline? FREE! ESPN?, FREE! FoxSports? FREE! Maybe if you wrote applications that ran across browsers and OSes, you'd generate enough advertising revenue to offer it for free, but until then, I'm not playing your crappy fantasy games either.<br /><br />And I'm not using your mail application. Trying to make it look like Outlook doesn't make it better. GMail doesn't look like Outlook, but those hackers over at Google innovate. Really innovate, not the fake innovation your potential bed fellows at Microsoft claim to do. They innovate by doing things new and different. Sorta like in the spirit of what the word <a href="http://dictionary.reference.com/browse/innovate">innovate</a> actually means. I don't know how I tolerated that spam infested Outlook copying piece of garbage for as long as I did.<br /><br />And my.yahoo.com? Kicked to the curb. And I'm not using your search engine either. Not that it's a bad search engine, it's just that it's not Google. And you are Yahoo! At least your shenanigans make clear <a href="http://dictionary.reference.com/browse/yahoo">how you picked you name</a>.<br /><br />So you can count me as just one not Windows using potential revenue source that you no longer have. Never ever will yahoo.com enter my browser in any form what so ever. I'm sure I won't be missed. After all, you'll be able to con enough people into using your crap to keep you afloat. I mean, look at all those people who fall for Nigerian phishing scams. I'm sure you look at them as potential clients. Not that targeting Windows users is a bad strategy. Last I checked it's still the most popular OS. <a href="http://www.tgdaily.com/content/view/41291/140/">Falling like a rock</a>. But still number one. No, it's that targeting Windows users is the least common denominator. Your stuff feels like Google stole all your real developers, and you have to make due with ASP.NET hackers who don't know a COM object from an ActiveX control (Hint, they're the same thing 'cause when COM/OLE failed, Microsoft <a href="http://en.wikipedia.org/wiki/ActiveX#History">rebranded it as ActiveX</a>). I don't believe that, it just feels that way when I use, or used to use, your crap. I trust you're doing the best you can. Your best is just not good enough anymore.<br /><br />Ciou!<br /><br />Don't know about you, but I feel better :)Curtis Cooleyhttp://www.blogger.com/profile/13559671537311158727noreply@blogger.com1tag:blogger.com,1999:blog-7507983247248838078.post-63732049314957856562009-08-27T13:54:00.000-07:002012-03-07T10:00:50.582-08:00My Broken Test Found My Missing ObjectI'm writing a rails application off and on for the last six months or so. Yesterday I started a major architectural change which broke a few tests. Getting those tests to pass proved pretty difficult until I finally realized what the tests were trying to tell me: "You are missing a domain object."<br />
<br />
I'm writing a <a href="http://thecooleys.org/">chore tracking tool</a> for families. You enter the chores you need done on a weekly basis, and it helps organize them by letting you assign due dates and family members to do the chore. I call the family members 'doers'.<br />
<br />
Initially, I had a User model and a Doer model. The User was the login, and the Doers were the ones who were assigned to chores. Perhaps you see the problem: doers did not have a way to login. That meant the User had to give her user credentials to all the doers, so each doer could update his or her chore. This also meant doers could reassign chores they didn't want to do or change due dates, or delete the chore altogether.<br />
<br />
I came up with two solutions, because you can't pick the simplest until you have at least two. First, I could copy a lot of the User login code over to the DoerController and then check against both models. This would lead to a lot of duplication until I could refactor it out. Second I could remove Doer and just have Users with different roles. This would involve more changes (possibly).<br />
<br />
I drew a little <a href="http://www.cognexus.org/IBIS-A_Tool_for_All_Reasons.pdf">IBIS</a> chart in my notes and determined idea two would be the best long term solution.<br />
<br />
I created a migration that deleted the doer table, added the fields from Doer to User, and copied all the Doers into the Users table. I then began to fix all the code this broke. When I got to the tests that were failing, I ran into a little issue. See, I added a field to User called user_id that linked Users playing the role of Doers to the User with the login credentials. This created a problem with my rails fixtures because I needed the fixtures to assign a user_id to a User before the User was created. The User didn't have an id yet. ActiveRecord doesn't create an id until the model is saved. I was in a paradox.<br />
<br />
The real problem was that I was using User to determine which Board the Doers belonged to. But I didn't even have a Board model. And I need one. That's what the failing tests were trying to tell me. And that is why tests and <a href="http://en.wikipedia.org/wiki/5_Whys">the five whys</a> are so important. If the tests would have just worked, I never would have had the opportunity to start digging into why the tests didn't work.<br />
<br />
Imagine this conversation going on in my head (which I strongly advise you don't do very often):<br />
<br />
Me: The tests are failing!<br />
Me: Why are the tests failing?<br />
Me: Because the fixtures don't know how to load user_id's of Users that don't exist yet.<br />
Me: Why are you trying to load user_id's of Users that don't exist yet?<br />
Me: Because each user determines which set of chores the user_doers have access to.<br />
Me: Why is that a User responsibility?<br />
Me: Because the User is the one who owns the board.<br />
Me: What board?<br />
Me: A HA!<br />
<br />
Here's my best secret of consulting: a well thought out question goes a lot farther than even the best proposed solution.<br />
<br />
So, test and test first. When you are stuck, ask why until you are unstuck.Curtis Cooleyhttp://www.blogger.com/profile/13559671537311158727noreply@blogger.com4tag:blogger.com,1999:blog-7507983247248838078.post-81849242402978673932009-06-25T08:48:00.000-07:002009-08-27T16:48:15.882-07:00Pair Programming != Two KeyboardsAt my previous job we pair programmed because we thought we were doing XP. I could go on and on about why we weren't, and perhaps I will in another post as an example of what <span style="font-style: italic;">not</span> to do, but I won't. I want to write about the paring area and one mistake we made: two keyboards and mice at each station. Don't do that. Here's why.<br /><br />Pair programming is about communication. Two developers sitting at the same workstation solving the same problem in tandem. Two heads are better than one, so to speak. Pair programming works best when the communication is at its highest. If you can't communicate about wanting the keyboard, how good is the rest of the communication going to be.<br /><br />Pair programming is about roles or 'hats'. There is a driver and a navigator. The driver worries about the language, syntax, formatting, and other low level programming things. The navigator worries about the task at hand, the design of the solution, and keeping the pair on track towards that solution. When those roles are reversed, there needs to be a clear indication. It's a context switch, and the pair needs a little switch time to orient to the new role. The act of passing the keyboard and mouse is the signal that the roles have been reversed.<br /><br />Pair programming is about cooperation. It's hard to work together in a field where working alone has been the norm. At the risk of leaning on stereotypes, programmers tend to be solitary creatures. Sharing a keyboard and mouse is a baby step towards the cooperation the pair will need to work effectively together.<br /><br />I'll always remember my supervisor coming to me after a few weeks working with one of the developers in the new pairing area and saying, "It seems like Joe is just not engaged when we pair. Any idea why?" I said because you have two keyboards and two mice. You have yours and Joe has his. He can focus on his set and think about what he'll do when it's his turn to use them, and not on what you are doing. When you both have a keyboard in front of you, the role each of you is supposed to play is blurred.<br /><br />So if you have two keyboards and two mice in your pairing area, remove one set for a few weeks. Have the pairs pass the keyboard back and forth for a while. Once they get good at it, which can take a long time, ask them how they feel about one keyboard and mouse. Remind them of the things I've written about pair programming, and have them focus on which hat they are wearing when. I predict that once they get good at it, they'll prefer the one keyboard and mouse, communication between pairs will improve, and productivity might even improve as the role each pair plays are made clear so progress towards task completion is accelerated.Curtis Cooleyhttp://www.blogger.com/profile/13559671537311158727noreply@blogger.com4tag:blogger.com,1999:blog-7507983247248838078.post-73599740347488759812009-06-07T10:36:00.000-07:002009-06-07T10:44:48.310-07:00TDD and FantaticsI don't understand how or when TDD == Fanaticism? Yes, TDD is a high discipline practice, but so is using SCM. You don't just arbitrarily decide to not check in code. Or start the day without pulling down the latest changes. Or not branch/label/snapshot the tree each release.<br /><br />Yes, TDD advocates can sound like zealots, and that's <a href="http://blog.objectmentor.com/articles/2009/03/10/lets-hear-it-for-the-zealots">OK.</a><br />We sound like zealots because we've done TDD long enough to get good at it. We are good enough at it to see the benefits. And it took a little longer than half a day to get good at it. We remember the code we wrote and read pre-TDD, and we prefer the code we write and read now with TDD. <br /><br /><a href="http://curtiscooley.com">I've been an XP consultant/coach</a> for 8 years now, and these sorry assed excuses about how TDD doesn't scale, or my process/project/team is unique and it doesn't fit, or I'm such a bad ass programmer that I don't need no stinkin best practice telling what to do, are getting really old. To be completely honest, they all have a slight aroma of laziness to me.<br /><br />There are a lot of best practices and processes out there that I do not do or do not advocate, but that's all I do. I don't go out of my way to bash them or the people who find them valuable.Curtis Cooleyhttp://www.blogger.com/profile/13559671537311158727noreply@blogger.com2tag:blogger.com,1999:blog-7507983247248838078.post-34024938101475839932009-03-23T21:07:00.000-07:002012-03-07T09:57:03.835-08:00MovingI'm <b>not</b> moving my blog to my new home page at <strike>http://curtiscooley.com</strike>. The main reason is I'm starting my own software contracting and agile consulting company. At first it'll just be me trying to find companies willing to give me money in return for helping them build software. My main value add will be my many years of agile consulting. Not that I'll try to convert shops to agile, but just that my agile experience has taught me how to maximize value to the customer.<br />
<br />
Thank you all for following me here and I hope you follow me to my new site.<br />
<br />
You can also follow me on twitter:CurtisRCooley<br />
<br />
Also, my new blogging software does a much much better job of formatting source code, as you'll see by my first post there: Why You Should Program To Interfaces Part ICurtis Cooleyhttp://www.blogger.com/profile/13559671537311158727noreply@blogger.com0tag:blogger.com,1999:blog-7507983247248838078.post-31027154423608836202009-03-11T11:17:00.000-07:002009-03-11T11:20:18.994-07:00Are We Asking Too Much From Programmers?In the olden days software teams were split into two distinct groups: architects and programmers. Architects designed the system and programmers implemented the design.<br /><br />Then Extreme Programming came along and said splitting teams up wasn't a good idea. Everyone should be in the same room. Collective code ownership said that anyone should be able to work on any piece of code. Now architects had to code and programmers had to design.<br /><br />Architects advanced to architect from programmer, so they'd coded before. The transition to writing code, while humbling, was not a huge task.<br /><br />Programmers were programmers because they were not architects. They had not designed before. This transition is more troubling.<br /><br />I'll cop out a little and lean back on the age old construction metaphor. See, construction workers aren't architects. Most of them don't even strive to be architects. They build things with their tools and their hands and enjoy it, why would they want to build things with paper and pencil?<br /><br />In agile development we are asking programmers to design. Maybe they don't want to design. Maybe, for some reason, they can't design. It's not part of their mental makeup. They construct. They are good and constructing. Why are you asking me to do more than construct?<br /><br />I've read and signed the Craftsmanship Manifesto, http://manifesto.softwarecraftsmanship.org/ and joined their Google group, http://groups.google.com/group/software_craftsmanship/web/the-new-left-side?pli=1. There is a thread in the group titled something like "Should we/can we convince programmers to care about their craft?" This thread got me thinking and that thinking lead to this blog.<br /><br />So, are we, the Agilists and craftsmen, asking too much from programmers?Curtis Cooleyhttp://www.blogger.com/profile/13559671537311158727noreply@blogger.com2tag:blogger.com,1999:blog-7507983247248838078.post-47492502654516906862009-03-10T09:05:00.000-07:002009-03-10T09:03:29.593-07:00OO Basics: Create the right objectMost people learn structured programming before they are exposed to object oriented programming. That's just the nature of the beast, since you need to learn the basics of a language before you can move on. At least that's the latest theory. The problem with structured programming is you learn to use flags to signal changes in state. You then end up with ifs and switches all over the code. For example<br /><br /><pre><br />if (item.isTaxable()) {<br /> item.setCost(item.getCost *= TAX_RATE);<br />}<br /></pre><br /><br />The root of the problem is that you only have one kind of Item.<br /><br /><pre><br />public class Item {<br /> private boolean taxable;<br /><br /> public boolean isTaxable() {<br /> return taxable;<br /> }<br /> ...<br />}<br /></pre><br />where two types are needed:<br /><pre><br />public interface Item {<br /> double getTax();<br />}<br /><br />public class TaxableItem implements Item {<br /> public double getTax() {<br /> return cost * TAX_RATE;<br /> }<br />}<br /><br />public class NontaxableItem implements Item {<br /> public double getTax() {<br /> return 0;<br /> }<br />}<br /></pre><br />You also have a problem with how the object is created:<br /><pre><br />Item item = new Item(true);<br /></pre><br /><br /><br />Say you are implementing a shopping cart, and as the user adds items to the cart, the correct type of item is added. Then at the end, you can calculate tax for the taxable items because the cart already contains the right kind of item.<br /><pre><br />cart.addItem(ItemFactory.createItem(itemId));<br /></pre><br />Now there are no ifs in the domain code:<br /><pre><br />for (Item item : cart.items()) {<br /> tax += item.getTax();<br />}<br /></pre><br />The moral: a lot of ifs and their accompanying headaches go away when you take the effort to create the right object.Curtis Cooleyhttp://www.blogger.com/profile/13559671537311158727noreply@blogger.com2tag:blogger.com,1999:blog-7507983247248838078.post-39099824639926930972008-12-23T13:53:00.000-08:002008-12-23T15:05:27.972-08:00RefactoringPerhaps there are already enough refactoring examples, but if not then I present yet another. I was writing some code investigating a potential project here at Venture, and since it was just <em>spike,</em> code, I just <em>hammered it out</em>. Here's <code>run()</code> from a <code>Runnable:</code><br /><pre><br /> public void run() {<br /> Map<String, FileDetail> fileDetails = new Hashtable<String, FileDetail>();<br /> try {<br /> BufferedReader reader = new BufferedReader(new FileReader(MainFrame.WATCHED_DATA));<br /> String directoryName = reader.readLine();<br /> File fileData = new File(directoryName, ".vobas");<br /> while (directoryName != null) {<br /> if (!fileData.exists()) {<br /> fileData.createNewFile();<br /> } else {<br /> ObjectInputStream fileDetailsReader = new ObjectInputStream(new FileInputStream(fileData));<br /> FileDetail fileDetail = (FileDetail) fileDetailsReader.readObject();<br /> while (fileDetail != null) {<br /> fileDetails.put(fileDetail.getName(), fileDetail);<br /> try {<br /> fileDetail = (FileDetail) fileDetailsReader.readObject();<br /> } catch (EOFException e) {<br /> break;<br /> }<br /> }<br /> }<br /> File[] files = new File(directoryName).listFiles();<br /> for (File file : files) {<br /> FileDetail fileDetail = fileDetails.get(file.getName());<br /> if (fileDetail == null) {<br /> ScpTo.send(directoryName + File.separatorChar + file.getName());<br /> fileDetails.put(file.getName(), new FileDetail(new Date(), file.getName()));<br /> } else if (file.lastModified() > fileDetail.getModificationDate().getTime()) {<br /> ScpTo.send(directoryName + File.separatorChar + file.getName());<br /> fileDetails.remove(file.getName());<br /> fileDetails.put(file.getName(), new FileDetail(new Date(), file.getName()));<br /> }<br /> }<br /> ObjectOutput objectOutput = new ObjectOutputStream(new FileOutputStream(new File(directoryName, ".vobas")));<br /> for (FileDetail fileDetail : fileDetails.values()) {<br /> objectOutput.writeObject(fileDetail);<br /> }<br /> objectOutput.close();<br /> directoryName = reader.readLine();<br /> }<br /> reader.close();<br /> } catch (FileNotFoundException e) {<br /> // TODO Auto-generated catch block<br /> e.printStackTrace();<br /> } catch (IOException e) {<br /> // TODO Auto-generated catch block<br /> e.printStackTrace();<br /> } catch (ClassNotFoundException e) {<br /> // TODO Auto-generated catch block<br /> e.printStackTrace();<br /> }<br /> }<br /></pre><br /><p>If you're anything like me, you'll need a few minutes to compose yourself after reading this. If you read that code and didn't immediately feel a little ill, I strongly suggest you pick up <em>Clean Code</em> by Robert Martin. Code like this should make you at least a little queezy.</p><br /><h3>Abstractness In Methods</h3><br />I learned from <em>Clean Code</em> that all methods should have the same level of abstractness. Public highlevel methods should do high level things. They should be composed of medium abstract methods that are themselves composed of highly detailed methods that do lowlevel things.<br /><br />For example this code<br /><pre><br />ObjectOutput objectOutput = new ObjectOutputStream(new FileOutputStream(new File(directoryName, ".vobas")));<br />for (FileDetail fileDetail : fileDetails.values()) {<br /> objectOutput.writeObject(fileDetail);<br />}<br />objectOutput.close();<br /></pre><br />writes FileDetails to a file. It can be extracted into a self describing method<br /><pre><br />writeFileDetails(fileDetails, directoryName);<br />...<br /></pre><br />where <code>writeFileDetails</code> looks like<br /><pre><br />private ObjectOutput writeFileDetails(Map<String, FileDetail> fileDetails, String directoryName)<br /> throws IOException, FileNotFoundException {<br /> ObjectOutput objectOutput = new ObjectOutputStream(new FileOutputStream(new File(directoryName, ".vobas")));<br /> for (FileDetail fileDetail : fileDetails.values()) {<br /> objectOutput.writeObject(fileDetail);<br /> }<br /> objectOutput.close();<br /> return objectOutput;<br />}<br /></pre><br />The section just above that starts with <code>File[] files = new File(directoryName).listFiles();</code> reads the files from the directory, checks to see if each file needs passed to ScpTo, then updates the FileDetail map. With judicious inlining, renaming, and extract method the <code>for loop</code> can be refactored to<br /><pre><br />for (File file : eachFileIn(directoryName)) {<br /> if (fileModified(file, fileDetails.get(file.getName()))) {<br /> ScpTo.send(directoryName + File.separatorChar + file.getName());<br /> fileDetails.put(file.getName(), new FileDetail(new Date(), file.getName()));<br /> }<br />}<br /></pre><br />and then replaced with a self describing method<br /><pre><br />sendModifiedFiles(fileDetails, directoryName);<br /></pre><br />The fileDetails Map is starting to look pretty interesting to me. It seems this <em>naked</em> collection should be made a first class object. I'll repress this urge for now, but I wanted to mention it during the same part of the process that it occurred to me.<br /><br />My run method is starting to fit on one page. Always a good sign. Arguments over method length can be as heated and never ending as brace wars, but while we can't seem to agree on one or two line methods, most can agree they should fit on one page in your editor.<br /><br />The if/else clause is about reading from the file detail file and loading up the fileDetails Map. Let's extract that method so <br /><pre><br />if (!fileData.exists()) {<br /> fileData.createNewFile();<br />} else {<br /> ObjectInputStream fileDetailsReader = new ObjectInputStream(new FileInputStream(fileData));<br /> FileDetail fileDetail = (FileDetail) fileDetailsReader.readObject();<br /> while (fileDetail != null) {<br /> fileDetails.put(fileDetail.getName(), fileDetail);<br /> try {<br /> fileDetail = (FileDetail) fileDetailsReader.readObject();<br /> } catch (EOFException e) {<br /> break;<br /> }<br /> }<br />}<br /></pre><br />becomes<br /><pre><br />readFileDetails(fileDetails, fileData);<br /></pre><br />Now we can extract the try clause into a very abstract method.<br /><pre><br />try {<br /> BufferedReader reader = new BufferedReader(new FileReader(MainFrame.WATCHED_DATA));<br /> String directoryName = reader.readLine();<br /> File fileData = new File(directoryName, ".vobas");<br /> while (directoryName != null) {<br /> readFileDetails(fileDetails, fileData);<br /> sendModifiedFiles(fileDetails, directoryName);<br /> writeFileDetails(fileDetails, directoryName);<br /> directoryName = reader.readLine();<br /> }<br /> reader.close();<br />}<br /></pre><br />becomes<br /><pre><br />try {<br /> sendModifiedFiles(fileDetails);<br />}<br /></pre><br />The two page try clause has been reduced to a single method call that explains succinctly what the method is trying to do. By breaking the long try clause into small chunks and extracting self describing methods, I've converted a fairly incomprehensible chunk of code into a self documenting class. I'll save wrapping the file details Map in a class and moving most of the methods to it since it seems to be the one doing all the work for another entry. Until then, here is the resulting class:<br /><pre><br />public class VobasBackupService implements Runnable {<br /><br /> public void run() {<br /> Map<String, FileDetail> fileDetails = new Hashtable<String, FileDetail>();<br /> try {<br /> sendModifiedFiles(fileDetails);<br /> } catch (FileNotFoundException e) {<br /> // TODO Auto-generated catch block<br /> e.printStackTrace();<br /> } catch (IOException e) {<br /> // TODO Auto-generated catch block<br /> e.printStackTrace();<br /> } catch (ClassNotFoundException e) {<br /> // TODO Auto-generated catch block<br /> e.printStackTrace();<br /> }<br /> }<br /><br /> private void sendModifiedFiles(Map<String, FileDetail> fileDetails) throws FileNotFoundException, IOException,<br /> ClassNotFoundException {<br /> BufferedReader reader = new BufferedReader(new FileReader(MainFrame.WATCHED_DATA));<br /> String directoryName = reader.readLine();<br /> File fileData = new File(directoryName, ".vobas");<br /> while (directoryName != null) {<br /> readFileDetails(fileDetails, fileData);<br /> sendModifiedFiles(fileDetails, directoryName);<br /> writeFileDetails(fileDetails, directoryName);<br /> directoryName = reader.readLine();<br /> }<br /> reader.close();<br /> }<br /><br /> private void readFileDetails(Map<String, FileDetail> fileDetails, File fileData) throws IOException,<br /> FileNotFoundException, ClassNotFoundException {<br /> if (!fileData.exists()) {<br /> fileData.createNewFile();<br /> } else {<br /> ObjectInputStream fileDetailsReader = new ObjectInputStream(new FileInputStream(fileData));<br /> FileDetail fileDetail = (FileDetail) fileDetailsReader.readObject();<br /> while (fileDetail != null) {<br /> fileDetails.put(fileDetail.getName(), fileDetail);<br /> try {<br /> fileDetail = (FileDetail) fileDetailsReader.readObject();<br /> } catch (EOFException e) {<br /> break;<br /> }<br /> }<br /> }<br /> }<br /><br /> private void sendModifiedFiles(Map<String, FileDetail> fileDetails, String directoryName) {<br /> for (File file : eachFileIn(directoryName)) {<br /> if (fileModified(file, fileDetails.get(file.getName()))) {<br /> ScpTo.send(directoryName + File.separatorChar + file.getName());<br /> fileDetails.put(file.getName(), new FileDetail(new Date(), file.getName()));<br /> }<br /> }<br /> }<br /><br /> private boolean fileModified(File file, FileDetail fileDetail) {<br /> return fileDetail == null || file.lastModified() > fileDetail.getModificationDate().getTime();<br /> }<br /><br /> private File[] eachFileIn(String directoryName) {<br /> return new File(directoryName).listFiles();<br /> }<br /><br /> private ObjectOutput writeFileDetails(Map<String, FileDetail> fileDetails, String directoryName)<br /> throws IOException, FileNotFoundException {<br /> ObjectOutput objectOutput = new ObjectOutputStream(new FileOutputStream(new File(directoryName, ".vobas")));<br /> for (FileDetail fileDetail : fileDetails.values()) {<br /> objectOutput.writeObject(fileDetail);<br /> }<br /> objectOutput.close();<br /> return objectOutput;<br /> }<br />}<br /></pre>Curtis Cooleyhttp://www.blogger.com/profile/13559671537311158727noreply@blogger.com2tag:blogger.com,1999:blog-7507983247248838078.post-41287379279945907612008-11-25T07:37:00.000-08:002008-11-25T07:43:42.249-08:00If you build it they will comeBill posted a comment to my last blog about seeing long methods and classes with too many responsibilities rather than the other way around. I posted a reply, but I though the reply was worth a short blog.<br /><br />I like to keep a close eye on primitive instance variables, including Strings. When they start to look at all interesting, I make a class out of them. By interesting I mean that code starts to care about more than just the value. Perhaps decisions are being made, or they are used to influence other instance variables. Perhaps a groups of instance variables are palling around together. Those are all signs of a missing object. Even if it starts out as just a data object with a constructor and getters. Objects are like the Field of Dreams, if you build them, behavior will come.Curtis Cooleyhttp://www.blogger.com/profile/13559671537311158727noreply@blogger.com5tag:blogger.com,1999:blog-7507983247248838078.post-70010990368043696342008-10-03T11:00:00.000-07:002008-10-03T10:57:00.829-07:00How to Write Great OO Day OneThere's no shortcut to experience. Writing good object oriented code takes experience, but here are three practices to help you get off on the right foot day one with even the grumpiest of gray beards:<br /><ol><br /><li>Write all your code using Test Driven Development(TDD)</li><br /><li>Follow the <a href="http://xp.c2.com/XpSimplicityRules.html">Rules of Simplicity</a></li><br /><li>Tell Don't Ask</li><br /></ol><br /><h3>Write All Your Code Using TDD</h3><br />Code written <a href="http://ponderingobjectorienteddesign.blogspot.com/2008/09/tdd-is-design-activity.html">test first</a> and code written test last is very very different code. Code written test first is loosely coupled and highly cohesive. Code written test last often breaks encapsulation when some property or private method needs to be exposed to the test because the class wasn't designed to be tested. If you write your code test first, your dependencies will be better and your code will be loosely coupled and highly cohesive. More on this later.<br /><h3>Follow the Rules of Simplicity</h3><br />Code is simple when it:<br /><ol><br /><li>Runs all the tests</li><br /><li>Contains no duplication</li><br /><li>Expresses all the intent</li><br /><li>Utilizes the fewest classes and methods</li><br /></ol><br />It's important to notice that I've used an ordered list. The order is important. A single GodClass with a single main() method is not simple. It may run all the tests, but in any program more complex than "Hello, world!" it certainly will contain duplication and not express all the intent.<br /><br>My struggle with the Rules of Simplicity focused around the <a href="http://ponderingobjectorienteddesign.blogspot.com/2008/09/if-bugs.html">If Bug</a>. I didn't understand how following the rules of simplicity would head off someone writing if heavy code. One could argue, and I tried, that if heavy code does not express intent. But when you read code like<br /><pre><br />if (mobile.getType() == MobileTypes.STANDARD) {<br /> alert();<br />}<br /></pre><br />It's pretty darn easy to see the intent. In the context of whichever method this is in, if the mobile is of type STANDARD then alert. How much more intent do you need?<br /><br>Then I had a little epiphany. If there's code like that, then certainly in other places in the code there's more. There's probably code like:<br /><pre><br />if (mobile.getType() == MobileTypes.GAS) {<br /> registerGasReading();<br />}<br /></pre><br />and<br /><pre><br />if (mobile.getType() == MobileTypes.TEXT) {<br /> sendTextMessage();<br />}<br /></pre><br />and<br /><pre><br />if (mobile.getType() == MobileTypes.LOCATION) {<br /> notifyLocation();<br />}<br /></pre><br />Do you see it? I sure do. Violations of rule 2. Many many violations of rule 2. And the worst kind of violations of rule 2. Duplication in many different pieces of the code. Duplication that is going to be very very hard to find. So to help prevent this, I've included<br /><h3>Tell Don't Ask</h3><br /><a href="http://www.pragmaticprogrammer.com/articles/tell-dont-ask">Tell Don't Ask</a> simply means don't ask an object about it's state and then do something. Tell the object to do something. This means all those if examples become:<br /><pre><br />mobile.alert();<br /></pre><br />and<br /><pre><br />mobile.registerGasReading();<br /></pre><br />and<br /><pre><br />mobile.sendTextMessage();<br /></pre><br />and<br /><pre><br />mobile.notifyLocation();<br /></pre><br />Now suppose that there were some of those if clauses splattered throughout the code that had duplicate implementations. In the if heavy version, they would be very hard to find, but in the tell don't ask version, all the implementations are in Mobile. All in one place and ready for sniffing out and eradicating.<br /><br><br />Listening to your tests will also help you keep your code simple.<br /><pre><br />public interface Alarm {<br /> void alert(Mobile mobile);<br />}<br /><br />public class Siren implements Alarm {<br /> public void alert(Mobile mobile) {<br /> if (mobile.getType == MobileTypes.STANDARD) {<br /> soundSiren();<br /> }<br /> }<br />}<br /><br />public class TestSiren extends TestCase {<br /> public void test_alert() {<br /> LocationMobile mobile = new LocationMobile();<br /> Siren siren = new Siren();<br /> siren.alert(mobile);<br /> assert(sirenSounded());<br /> }<br />}<br /></pre><br />If you listened closely to your test, it would be asking you, "Why do you need a LocationMobile to test the Siren?" Why indeed? It seems that Sirens shouldn't even know about LocationMobiles.<br /><pre><br />public class LocationMobile {<br /> private Alarm alarm;<br /> public LocationMobile(Alarm alarm) {<br /> this.alarm = alarm;<br /> }<br /> public void alert() {<br /> alarm.alert(); // alert on Alarm no longer needs a mobile<br /> }<br />}<br /><br />public class TestLocationMobile() extends TestCase {<br /> public void test_alert() {<br /> Alarm alarm = EasyMock.createMock(Alarm.class);<br /> alarm.alert();<br /> EasyMock.replay(alarm);<br /> Mobile mobile = new LocationMobile(alarm);<br /> mobile.alert();<br /> EasyMock.verify(alarm);<br />}<br /></pre><br />It looks like I've just swapped the dependencies. Instead of Alarm depending on Mobile, I now have Mobile depending on Alarm. But if you look closely at the test, the real dependency was Siren knowing about LocationMobile. A concrete class depended on another concrete class. This violates the <a href="http://c2.com/cgi/wiki?DependencyInversionPrinciple">Dependency Inversion Principle</a>(DIP). The second example has LocationMobile depending on the interface Alarm. A concrete class depending on an abstraction. This satisfies DIP.<br><br />If you write all your code TDD, follow the Rule of Simplicity, and Tell Don't Ask then you'll be on the path to becoming a better OO programmer. Good OO code is easy to read and maintain, but can be hard to write. At least at first. The more you write the better you'll become, and the more experience you will get. In the meantime, these practices should get you well on your way.Curtis Cooleyhttp://www.blogger.com/profile/13559671537311158727noreply@blogger.com4tag:blogger.com,1999:blog-7507983247248838078.post-69174933523326835902008-09-19T09:18:00.000-07:002008-09-23T12:45:29.450-07:00TDD is a Design ActivityThis has been written and talked about before, and if you've read Kent Beck's <a href="http://www.amazon.com/Test-Driven-Development-Addison-Wesley-Signature/dp/0321146530">TDD book</a>, you'll understand how much it's emphasized, but I still see a lot of test code that is about validation and very little about design. And it doesn't need to be that way. Simply follow the <a href="http://butunclebob.com/ArticleS.UncleBob.TheThreeRulesOfTdd">three rules of TDD</a>:<br /><ol><br /><li>Only write production code that makes a failing unit test pass.</li><br /><li>Write the minimum amount of test code to get production code to fail. Not compiling counts.</li><br /><li>Write only enough production code to get a test to pass.</li><br /></ol><br />What I see the most from the test code I read is violations of rule two. Which is very understandable. Why would you not want to write more test code? Because tests, just like every other piece of the system, have three functions:<br /><ol><br /><li>implement the desired feature</li><br /><li>afford change</li><br /><li>reveal intent</li><br /></ol><br />Writing line after line of test code to cover every possible corner condition certainly meets the first, but violates the second and third. I've seen test code that looks something like:<br /><pre><br />public void testServicesStopped() {<br /> assertServicesStoppedIfEnabled(false, false, false, false, false, false);<br /> assertServicesStoppedIfEnabled(true, false, false, false, false, false);<br /> assertServicesStoppedIfEnabled(false, true, false, false, false, false);<br /> assertServicesStoppedIfEnabled(false, false, true, false, false, false);<br /> assertServicesStoppedIfEnabled(false, false, false, true, false, false);<br /> assertServicesStoppedIfEnabled(true, true, false, false, false, false);<br /> assertServicesStoppedIfEnabled(false, true, true, false, false, false);<br /> assertServicesStoppedIfEnabled(false, false, true, true, false, false);<br /> assertServicesStoppedIfEnabled(true, true, true, false, false, false);<br /> assertServicesStoppedIfEnabled(false, true, true, true, false, false);<br /> assertServicesStoppedIfEnabled(true, true, true, true, false, false);<br /> // every possible combination was covered<br />}<br /></pre><br />Certainly a very thorough test, but also very very intention hiding. Unit tests should function as documentation for the system. You should be able to read the test and determine what a module does and how it does it. To make matters worse, <code>testServicesStopped()</code> was the first method in the test and <code>assertServicesStoppedIfenabled()</code> was near the bottom with many other tests and helper methods in between. To try and figure out what each boolean meant, you had to scroll up and down the file and try to remember 6 parameters. I would also bet a considerable sum that the bulk of this code was written after the production code was completely finished. Not only does this test code not reveal intent, but it's very rigid. It's hard to change because it's hard to understand. When the customer wants to change how this feature works, and the developer opens this test to write the failing test so he can write the feature changes, he'll have a very difficult time figuring out what to change.<br /><br />Having a suite of unit tests you can run to verify your system still functions is very important, but just as important is having those unit tests clearly document the system. The easiest way to achieve this is to use TDD the way it was designed to be used by following the Three Laws of TDD.Curtis Cooleyhttp://www.blogger.com/profile/13559671537311158727noreply@blogger.com0tag:blogger.com,1999:blog-7507983247248838078.post-36755405611870790212008-09-12T08:46:00.000-07:002008-09-23T13:31:53.937-07:00Lazy Inheritance BugsI ran across another type of bug yesterday. I'm calling it the <span style="font-style: italic;">Lazy Inheritance Bug</span>. I'm not sure where I first heard the term <span style="font-style: italic;">lazy inheritance</span>, but I've always thought it was a good way to explain the power of delegation, and until yesterday didn't even realize that it could encompass a category of bugs.<br /><h3>Lazy Inheritance</h3><br />If you've done any object oriented development, you've probably heard the terms is-a and has-a. Is-a is a way to determine if one object should inherit from another. It's not the best criteria to use, but it is a pretty good rule of thumb. Unfortunately, some developers get into the habit of inheriting from another class just to get some reuse. Some method in a class does some work that they need, so they extend that class to get that work for free. That's <span style="font-style: italic;">lazy inheritance.</span><br /><h3>Lazy Inheritance Bugs</h3><br />When you use lazy inheritance, you begin a cascading hierarchy that can result in bugs. You extend some class to get some free work. Someone else extends your class to get some of your free work. Someone extends that class to get more free work until you have some methods very far down the hierarchy that depend on methods way up the hierarchy. Now, the author of the original class changes a method and something completely different breaks. That's the <span style="font-style: italic;">Lazy Inheritance Bug.</span><br /><br />And that's why terms like is-a exist. To give you some criteria from which to decide if you should inherit from another class. <a href="http://www.pmg.lcs.mit.edu/%7Eliskov/">Barbara Liskov</a> provided a better criteria called the <span style="text-decoration: underline;"></span><a href="http://www.objectmentor.com/resources/articles/lsp.pdf">Liskov Substitution Principle</a> (LSP). Basically, given a class A that inherits from class B then any class C that has a reference to class A should also work when given a reference to class B without having to know it has a B. In other words, class B should be substitutable for class A. When you use lazy inheritance, you violate the LSP by definition.<br /><br />Avoiding lazy inheritance bugs is easy: don't use inheritance unless you can conform to the LSP.<br /><h3>Delegation</h3><br />Before you use inheritance to get reuse or avoid duplication, take some time to consider delegation instead. Here's an example:<br /><br />Inheritance:<br /><pre><br />public class Car {<br /> public void go() {<br /> // goes pretty fast<br /> }<br />}<br /><br />public class SportsCar extends Car {<br /> public void go() {<br /> // goes really fast<br /> }<br />}<br /></pre><br />Notice the only difference between a Car and a SportsCar is the engine. We could use delegation and only have one type of car.<br /><pre><br />public interface Engine {<br /> void go();<br />}<br /><br />public class EconomyEngine implements Engine {<br /> public void go() {<br /> // goes pretty fast<br /> }<br />}<br /><br />public class SportsEngine implements Engine {<br /> public void go() {<br /> // goes really fast<br /> }<br />}<br /><br />public class Car {<br /> private Engine engine;<br /> <br /> public Car(Engine engine) {<br /> this.engine = engine;<br /> }<br /><br /> public void go() {<br /> engine.go();<br /> }<br />}<br /></pre><br />So now you can configure your car with whatever engine you want. This example doesn't seem to hold its weight. There's still inheritance going on, but now it's with Engine and not Car. What gives? Well, just add one more option to see how delegation almost always wins:<br /><pre><br />public class FamilyCar {<br /> public void go() {<br /> // goes pretty fast<br /> }<br /><br /> public void turn() {<br /> // turns OK<br /> }<br />}<br /><br />public class SportsCar {<br /> public void go() {<br /> // goes really fast<br /> }<br /><br /> public void turn() {<br /> // turns really well<br /> }<br />}<br /><br />public class SportSuv {<br /> public void go() {<br /> // goes really fast<br /> }<br /><br /> public void turn() {<br /> // turns OK<br /> }<br />}<br /><br />public class EconomyCar {<br /> public void go() {<br /> // goes pretty fast<br /> }<br /><br /> public void turn() {<br /> // turns really well<br /> }<br />}<br /></pre><br />How can we use inheritance to combine just these two features? With delegation it's easy:<br /><pre><br />public interface Engine {<br /> void go();<br />}<br /><br />public class SportsEngine implements Engine {<br /> public void go() {<br /> // goes really fast<br /> }<br />}<br /><br />public class EconomyEngine implements Engine {<br /> public void go() {<br /> // goes pretty fast<br /> }<br />}<br /><br />public interface Suspension {<br /> void turn();<br />}<br /><br />public class SportsSuspension implements Suspension {<br /> public void turn() {<br /> // turns really well<br /> }<br />}<br /><br />public class EconomySuspension implements Suspension {<br /> public void turn() {<br /> // turns OK<br /> }<br />}<br /><br />public class Car {<br /> private Engine engine;<br /> private Suspension suspension;<br /> public Car(Engine engine, Suspension suspension) {<br /> this.engine = engine;<br /> this.suspension = suspension;<br /> }<br /><br /> pubic void go() {<br /> engine.go();<br /> }<br /><br /> public void turn() {<br /> suspension.turn();<br /> }<br />}<br /></pre><br />Now we can configure any Car with any type of Engine and Suspension. When you start thinking about the braking system and traction control and air bags and body style and on and on, you can see that trying to use inheritance for each possible car configuration would get out of hand really fast. Delegation allows you to configure your car with each component and have the reuse in the components and not in the hierarchy. And that's how to squash lazy inheritance bugs before they hatch.Curtis Cooleyhttp://www.blogger.com/profile/13559671537311158727noreply@blogger.com5tag:blogger.com,1999:blog-7507983247248838078.post-5856036883585022882008-09-05T14:00:00.000-07:002008-09-05T15:11:25.323-07:00Naming the Flag ObjectSo, you've decided to avoid the <a href="http://ponderingobjectorienteddesign.blogspot.com/2008/09/if-bugs.html">If Bug</a> by using the <a href="http://ponderingobjectorienteddesign.blogspot.com/2008/09/flag-object.html">Flag Object</a>, but you can't seem to come up with a good name. Names should come from natural mappings not artificial ones. By that I mean the names of your objects should come from the customer's not the developers' vocabulary. Listen closely to what the customer says when she describes the story she wants implemented. When I asked my fictional customer, <blockquote>"How are managers' pay calculated differently from regular employees' pay?"</blockquote> she said, <blockquote>"Managers and employees are paid based on different pay scales."<br /><br /></blockquote>Now, I could have just listened to what she <span style="font-style: italic;">meant</span> and gone about implementing a PayCalculator, but I listened closely to what she <span style="font-style: italic;">said</span> and the words <span style="font-weight: bold;">pay scale</span> jumped out at me. That's how PayScale was born. Notice I chose a noun.<br /><br />Avoid words which do not come from either the customer or the developer domains. "ER" words are the worst: Manager, Calculator, Helper etc.. These words hide the intent of your object, make it hard for other developers to trace its responsibility back to a user story, and lay the groundwork for the object to become large. How many small, cohesive, single responsibility FooManager classes have you ever seen?<br /><br />When the customer comes to me later and says, <blockquote>"We've changed the manger's pay scale to take into account how many employees each manager manages,"</blockquote> I can go straight to the ManagerPayScale object and implement the changes instead of trying to figure out if the change is in the PayManager or EmployeePayCheckUtility or SuperPayRateHelper. The words <span style="font-style: italic;">manager</span> and <span style="font-style: italic;">pay scale</span> were written right in the feature request, so when I look in the paycheck package and see an object called ManagerPayScale chances are I won't have to look much further.<br /><br />Names are very very important in development. Object names, variable names, and method names can make your code easy to read, intent revealing, and pleasant to work with. On the other hand, poor names hide intent, make code difficult to understand, and painful to maintain. Put effort into choosing your names. And when you get to the refactoring stage, reevaluate your names. Sometimes they make perfect sense when you first came up with them, but later, in the full context of the code, they don't. So change them. If you are not in a situation in which you get to pair program, show your names to other developers. The names might have made perfect sense to you, but others are going to have to maintain your code, so let them have some input.<br /><br />Name your objects well and not only will other's enjoy maintaining your code, you'll have fewer bugs.Curtis Cooleyhttp://www.blogger.com/profile/13559671537311158727noreply@blogger.com3tag:blogger.com,1999:blog-7507983247248838078.post-87038930747181077292008-09-03T08:20:00.000-07:002015-11-05T11:43:40.740-08:00Flag ObjectI've worked with a lot of programmers that write <a href="http://ponderingobjectorienteddesign.blogspot.com/2008/09/if-bugs.html">if bugs</a>, so I've come up with small simple solutions. The simplest solution I've come up with so far for "if bugs" is what I call the Flag Object.<br />
<h3>
The Algorithm</h3>
<ol>
<li>Instead of making a boolean, make an object.</li>
<br />
<li>Take the if clause from the client code and make it a method on the object.</li>
<br />
<li>In the client code call the method on the object.</li>
</ol>
I said it was simple.<br />
<h3>
An Example</h3>
The Old Code<br />
<pre>public class Employee {
private boolean manager;
public boolean isManager() {
return manager;
}
...
}
...
Employee employee = new Employee();
if (employee.isManager()) {
// calculate manager pay
} else {
// calculate employee pay
}
...</pre>
<br />
This is a potential "if bug". The following is better and only slightly more 'work':<br />
<pre>public class Employee {
private PayScale payscale;
public double calculatePay() {
payscale.calculatePay();
}
...
}
public interface PayScale {
double calculatePay();
}
public EmployeePayScale implements PayScale {
public double calculatePay() {
// calculate employee pay
}
}
public ManagerPayScale implements PayScale {
public double calculatePay() {
// calculate manager pay
}
}
...
Employee employee = new Employee()
double pay = employee.calculatePay()
..</pre>
<br />
<br />
If you were doing the old boolean flag trick, you'd have places in the code to set the boolean true or false.<br />
<pre>employee.setManager(true);</pre>
<br />
Now you call something like:<br />
<pre>employee.setPayScale(new ManagerPayScale());</pre>
<br />
Now the details of how a manager's and employee's pay is calculated is encapsulated in the PayScale objects. How pay is calculated can change and not affect anyone who actually uses the code to calculate pay. You can also add new employee types and not have to change existing code. That means the code satisfies the <a href="http://www.objectmentor.com/resources/articles/ocp.pdf">Open-Closed Principle</a>.<br />
<br />
There a few things I don't like. One, the object that calls <br />
<pre>employee.setPayScale()</pre>
depends on the low level objects in the PayScale hierarchy. This breaks the <a href="http://www.objectmentor.com/resources/articles/dip.pdf">Dependency-Inversion Principle</a> that says concrete objects should depend on abstractions. Preferably the employee object is obtained from some kind of factory that knows how to make Employee objects already configured with the appropriate PayScale object. However, it's better than the "if bug" problem so I can live with a little dependency issue. At least the dependency issue is localized and easily solved. If Bugs can be spread throughout the code and finding all of them very very difficult. I also prefer Flag Objects because they conform to <a href="http://c2.com/cgi/wiki?TellDontAsk">Tell Don't Ask</a>.Curtis Cooleyhttp://www.blogger.com/profile/13559671537311158727noreply@blogger.com5tag:blogger.com,1999:blog-7507983247248838078.post-13399177753460199712008-09-02T08:44:00.000-07:002008-09-02T09:02:34.517-07:00If BugsI've run across a particularly nasty type of bug, and I really haven't seen anyone else talk about it, so I will. I call them "if bugs" and they occur when encapsulation is broken. They usually take the form of:<br /><pre><br />public class Employee {<br /> private boolean manager;<br /><br /> public boolean isManager() {<br /> return manager;<br /> }<br />...<br />}<br /><br />...<br />if (employee.isManager()) {<br /> calculateManagerPay();<br />else {<br /> calculatePay();<br />}<br />...<br /></pre><br />The problem is that now there are potentially many other parts of the system that care about whether an employee is a manager or not. Some of those other parts will not be changed, and defects will be reported against the system when those parts of the application are used. That's the "if bug".<br /><br />Encapsulation was broken when manager was exposed to the rest of the system. Object Oriented Design tells us to not expose the implementation details of a class to the rest of the system.<br /><br />There are many better ways to implement this, and I'll save what is perhaps the simplest way, Object Flag, for next time.Curtis Cooleyhttp://www.blogger.com/profile/13559671537311158727noreply@blogger.com5