It''s a Game As the saying goes, "Time flies when you''re having fun." I think this could easily be extended to "Time flies when you''re having fun developing software." It always amazes me how quickly the day goes by when I am in programmer mode. I am sure that in part it is because I am enjoying what I am doing. I love writing code and developing software; to me it is like a puzzle game.As a young kid learning to program, I had a good friend who was also keen to learn, and we would often sit together at my father''s Commodore computer and type in lines of code, suggesting ideas as we went and improving on each other''s code. It was a lot of fun, and we created some pretty cool little applications (at least, we thought they were).Upon learning of pair programming, I realized that as kids, my friend and I had been pair programming, and that if it was fun then it should surely be a lot of fun now.
Exercise 2-1: Game to Teach Pair Programming This game is based on another game called the Perfection Game, which can be found in Software for Your Head. You can play it with two or more people. To play this game, you need one PC with one keyboard, one mouse, .NET Framework development tools installed, enough chairs, and space for all developers to sit and see the screen. With more than two people, I suggest you use a projector so that all the participants can see what is going on.There are two roles in this game: programmer and supporter. Each person takes turns to be the programmer, while everyone else takes the role of the supporter.
1. | The programmer declares what the function he is going to write will do. He states his intention. I suggest something small that can be completed in less than 5 minutes. | 2. | When the programmer is finished, he declares he is done. | 3. | Each supporter in turn grades the function from 0 to 10, with 10 being the highest value and 0 being the lowest. For every point less than 10 that is given, the supporter must come up with a way of making it a 10.- a. The supporter then follows his grading with a statement of what he likes about the code as written.
- b. The supporter then states what it would take to get a 10.
| 4. | One of the supporters takes the role of the programmer, and the process begins again. | 5. | After everyone has had one turn at being the programmer, you can repeat the cycle, but this time the programmer starts with the code that he or she wrote before and tries to improve it for a couple of minutes to raise the grade. |
Here is an example round between two of the programmers in our eXtreme .NET team, Deepak and Sue. | Deepak: I''ll go first. I''m going to write a function that increments a private variable until it reaches a certain number and then resets it.Sue: Okay, go to it.<Deepak types the following> class PairGame { int var; public void Increment() { var++; if (var > 500) { var = 0; } } }
Deepak: Done!Sue: Cool. I give the code 5 out of 10. What I like about it is that it is concise, the naming of the class and method is clear, and the code is easy to read. To get a 10, the integer variable should be named something meaningful; var doesn''t mean much to me. The integer variable should be initialized somewhere. The limit and reset numbers, 500 and 0, should be in variables or constants to make them easier to change later. You should write a test to prove it works. If it had all that, I would give the function 10 out of 10.Deepak: Thanks, Sue.Sue: You''re welcomenow it''s my turn. |
| Pointers for the supporter: Keep the feedback positive. Say what you like and what it would take to get to 10. Do not say what you don''t like. Don''t be personal. Keep your comments focused on the product, not on the programmer. Phrasing the statements is important. It is better to say, "I like the way the function uses the increment operator" rather than "I like the way you used the increment operator." Remember you are scoring the product, not the programmer. Pointers for the programmer: Remember, it is not personal. The supporter is scoring the code, not you. Always remember to thank the supporter for the input. It is helping the product get better.
Exercise 2-2: Step-by-Step Exercise to Get a Feel for Pair Programming In this exercise, you focus on the pair programming aspect and do not worry about the other practices. This exercise can be done without having read any of the other chapters in this book. The aim is to help the two of you get a feeling for writing code as a pair.To do this exercise, you need one PC with one keyboard, one mouse, .NET Framework development tools installed, two chairs, enough space for both developers to sit in front of the PC and see the screen, and one copy of this exercise (i.e., this book).Start off with one of you controlling the PC (the driver) and the other holding the exercise (the navigator). The navigator is going to guide the driver through developing the code. At certain points, you will swap roles. It is very important that you do swap at the specified times and no later. While you are in the role of navigator, try not to show your partner the code as it is written in the exercise. You should attempt to verbally communicate what needs to be done. You can draw diagrams and as a last resort read out the line of code to be typed. If at any point either of you feel unsure that you have done the correct thing, it is time to compile and test what you have done. You should be able to compile and run the program after nearly every step.In keeping with the theme of pairing being fun, you are going to develop a screen saver together:
1. | Start off by setting up a Windows Forms application to work as a screen saver. Create a new C# Windows Forms application project called Extreme Screen Saver. | 2. | Change the name of the created form class to SaverForm. | 3. | Edit the constructor to take an integer parameter and store it in a member variable of the class called ScreenID. private int screenID; public SaverForm(int screen) { InitializeComponent(); screenID = screen; }
| 4. | Edit the Main method to use parameters to determine how to run the application. This is how Windows launches a screen saver.It might help if you can draw a table for your partner (on a white board or a scrap of paper). Argument | Meaning |
---|
/c | Display the config/settings screen | /s | Launch the screen saver | /p | Preview the screen saver | static void Main(string[] args) { if (args.Length > 0) { // config if (args[0].ToLower().Trim().Substring(0,2) == "/c") { } // saver else if (args[0].ToLower() == "/s") { } // preview else if (args[0].ToLower() == "/p") { } } else { } }
| 5. | Create a static protected method in the SaverForm class called LaunchSaver. In this LaunchSaver method, add code to iterate through all the screens connected to the PC and create a new form to run in that screen. static protected void LaunchSaver() { for (int i = Screen.AllScreens.GetLowerBound(0); i <= Screen.AllScreens.GetUpperBound(0); i++) { System.Windows.Forms.Application.Run(new SaverForm(i)); } }
| 6. | Back in the Main method, call this static method from the launch and default argument cases. static void Main(string[] args) { if (args.Length > 0) { if (args[0].ToLower().Trim().Substring(0,2) == "/c") { } else if (args[0].ToLower() == "/s") { SaverForm.LaunchSaver(); } else if (args[0].ToLower() == "/p") { } } else { SaverForm.LaunchSaver(); } }
| 7. | Compile and run the program. Close it, and then try renaming the created EXE file from eXtreme Screen Saver.exe to eXtreme Screen Saver.scr. You should be able to install this and run it as a screen saver. | 8. | Swap roles. | 9. | We will now set up the form to occupy the entire screen and respond to mouse and keyboard events to close down. In the design view for the Saver-Form, set the FormBorderStyle property to None. | 10. | Create an event handler for the Load event on the form. This can be done by double-clicking the form in design view. In this method, we need to set the Bounds of the form to those of the screen, hide the cursor, and set the form to display on top of all the other forms. private void SaverForm_Load(object sender, System.EventArgs e) { Bounds = Screen.AllScreens[screenID].Bounds; Cursor.Hide(); TopMost = true; }
| 11. | In this Load method, we can also add handler code for the mouse and keyboard events. We want to handle these events and decide whether we are going to end running the screen saver. Start by adding an event handler for the MouseMove event. Call this method SaverForm_MouseEvent. In Visual Studio.NET, you can type MouseMove += and then pressing the Tab key should generate the handler skeleton. private void SaverForm_Load(object sender, System.EventArgs e) { ... MouseMove +=new MouseEventHandler(SaverForm_MouseEvent); }
When the application first runs, it will get the mouse location through a mouse move event, so the first time we get the event, we just need to trap the position of the mouse in a member variable. After that, if the mouse moves, we need to close the application. private Point initialMousePt; private void SaverForm_MouseEvent(object sender, MouseEventArgs e) { if (!initialMousePt.IsEmpty) { if (initialMousePt != new Point(e.X, e.Y)) Close(); } initialMousePt = new Point(e.X, e.Y); }
| 12. | We can use this same event handler for the mouse down event that occurs when a user clicks a mouse button. Add the method to the event in the Load method. Then change the MouseEvent method to also close the form when a click has occurred. private void SaverForm_Load(object sender, System.EventArgs e) { ... MouseMove +=new MouseEventHandler(SaverForm_MouseEvent); MouseDown +=new MouseEventHandler(SaverForm_MouseEvent); } private void SaverForm_MouseEvent(object sender, MouseEventArgs e) { if (!initialMousePt.IsEmpty) { if (initialMousePt != new Point(e.X, e.Y)) Close(); if (e.Clicks > 0) Close(); } initialMousePt = new Point(e.X, e.Y); }
| 13. | Now let''s do the same with a handler for the KeyDown event. private void SaverForm_Load(object sender, System.EventArgs e) { ... KeyDown +=new KeyEventHandler(SaverForm_KeyDown); } private void SaverForm_KeyDown(object sender, KeyEventArgs e) { Close(); }
| 14. | Compile and run the program. It is starting to work more like a screen saver. Rename the EXE file to an SCR file and test it. We now have a basic screen saver. | 15. | Swap roles. | 16. | The next thing to do is to add some drawing functionality so that the screen saver actually does something. We will start by adding a Paint event handler to the Form class. Again we can do this in the Form load method. private void SaverForm_Load(object sender, System.EventArgs e) { ... Paint +=new PaintEventHandler(SaverForm_Paint); } private void SaverForm_Paint(object sender, PaintEventArgs e) { }
| 17. | In the Paint event method, we will add some code to draw the text eXtreme .NET in the center of the screen. We will use the TranslateTransform method to shift the drawn text to the center of the screen.You need to add the System.Drawing.Drawing2D namespace to this class file because we need to use the MatrixOrder enumerated type from there. private void SaverForm_Paint(object sender, PaintEventArgs e) { Graphics gc = e.Graphics; Point screenCenter = new Point( (int)(gc.VisibleClipBounds.Width/2), (int)(gc.VisibleClipBounds.Height/2)); gc.TranslateTransform(screenCenter.X, screenCenter.Y, MatrixOrder.Append); int fntSize = 12; Font fnt = new Font(FontFamily.GenericSansSerif,fntSize); Brush brsh = Brushes.Red; StringFormat format = new StringFormat(StringFormat.GenericTypographic); format.Alignment = StringAlignment.Center; format.LineAlignment = StringAlignment.Center; SizeF size = gc.MeasureString("eXtreme .NET", fnt); RectangleF rect = new RectangleF( 0 - size.Width/2, 0 - size.Height/2, size.Width, size.Height); gc.DrawString("eXtreme .NET", fnt, brsh, rect, format); }
| 18. | Add a timer control to the form and use the timer event to redraw the screen so that we can add some animation to the text. You do that in the design view. The timer control can be dragged from the toolbox onto the form and then double-clicked to generate the event handler method. Make sure you set the Enabled property of the timer to true so that it raises the events. | 19. | In the timer event handler, we will call Invalidate. private void timer1_Tick(object sender, System.EventArgs e) { Invalidate(); }
| 20. | Next animate the text. We will make the text grow and shrink on the screen using the ScaleTransform method of the Graphics object. To do this, we need some class member variables to represent the current scale and the amount to change the scaling. We can manipulate these in the timer tick event. private float scale = 1; private float scaleChange = 0.1F; private void SaverForm_Paint(object sender, PaintEventArgs e) { Graphics gc = e.Graphics; Point screenCenter = new Point( (int)(gc.VisibleClipBounds.Width/2), (int)(gc.VisibleClipBounds.Height/2)); gc.ScaleTransform(scale, scale, MatrixOrder.Append); gc.TranslateTransform(screenCenter.X, screenCenter.Y, MatrixOrder.Append); ... } private void timer1_Tick(object sender, System.EventArgs e) { scale+= scaleChange; if (scale < 1) { scaleChange = 0.1F; } if (scale > 20) { scaleChange = -0.1F; } Invalidate(); }
| 21. | Compile and run the program. You may want to decrease the Interval property on the timer to make the animation more viewable. | 22. | Swap roles. | 23. | We will use some of the built-in double buffering provided by the .NET Framework to remove the screen flicker. We will also add some more movement to the text animation. Before we add the double buffer in the constructor of our Form class, we need to set the style of the form to use WMPaint, the UserPaint style, and DoubleBuffer. public SaverForm(int screen) { InitializeComponent(); screenID = screen; this.SetStyle( ControlStyles.AllPaintingInWmPaint| ControlStyles.UserPaint| ControlStyles.DoubleBuffer, true); }
| 24. | Now let''s add rotation to the text animation so that it spins around as it grows and shrinks. We will use two class member variables to indicate the current angle of the text and the next amount by which to change the angle. We can vary this in our timer tick event method. In the Paint event method, we will use the RotateTransform method to rotate the text around the origin. private float angle = 0; private float angleChange = 5; private void SaverForm_Paint(object sender, PaintEventArgs e) { Graphics gc = e.Graphics; Point screenCenter = new Point( (int)(gc.VisibleClipBounds.Width/2), (int)(gc.VisibleClipBounds.Height/2)); gc.RotateTransform(angle, MatrixOrder.Append); gc.ScaleTransform(scale, scale, MatrixOrder.Append); . . . } private void timer1_Tick(object sender, System.EventArgs e) { angle+=angleChange; scale+= scaleChange; if (scale < 1) { scaleChange = 0.1F; angleChange = 5F; } if (scale > 20) { scaleChange = -0.1F; angleChange = -5F; } Invalidate(); }
| 25. | Compile and run the program. The animation should look much smoother. Try it as a screen saver by renaming the EXE file. |
This exercise should have provided you with an understanding of how often you should swap roles and how important it is for both of you to be involved with the code being developed. Pairing is no fun if you are just watching someone else write code. It also has very little value if that is all that occurs during a pair-programming session.Pair programming is about both developers being present and focused on the task at hand and both developers aiming  |