= 什么是Code Kata = Kata源自日语的招式,也是目前敏捷工程实践中比较热门的词汇,Code Kata是由Dave Thomas提出的。具体介绍可以[http://codekata.pragprog.com/ 猛击这里]。 Coding Dojo和Code Kata都是通过实际的编程练习来提升敏捷开发的技能。 它是一个学习的过程。一些程序员(通常是15-20人)在一起编程解决一个程序问题。一边编程,一边互相学习。每个人可以从中学到:解决问题的思路、编程技巧、面向对象设计、演进式设计、结对编程、测试驱动开发、持续集成等等。其实即使从别人犯的错误中,我们也能够学到很多。 = 组织形式 = 一个会议室中有一台电脑、一台投影仪、椅子若干。[[BR]] 在会议室中间有两个人结对编程,一个是驾驶员(Pilot),另一个是领航员(Co-pilot, Navigator)。周围坐的都是顾问(Advisor, Sensei),前面是投影仪。 * 驾驶员(Pilot):负责具体实现,边写代码,边要把自己的设计意图大声说出来 * 领航员(Co-pilot):负责帮助驾驶员进行设计,负责控制设计的战略方向 * 顾问(Advisor):坐在周围,通过投影仪观察当前的进展,可以提出问题,但是不能给出方案。驾驶员与领航员必须先回答顾问的所有问题,才能继续写代码。 每次结对时间为 7 分钟,时间到后领航员轮换成驾驶员,驾驶员下去当顾问,一名顾问轮换为领航员,以此循环。[[BR]] 一些原则 * 不要讨论太多,先写代码 * 写代码之前,先写测试 * 每个人都能从其他人身上学到东西,哪怕是错误 * 所有人参与写代码 * 如果你知道好的方案,分享给大家 * 顾问只能问问题,不能提方案,驾驶员和导航员必须回答所有问题 = 2013-05-10 && 2013-05-17 活动 = 为了减少对工作的影响,每周活动安排在周五下午,每次活动时间控制为2小时。 题目由参与同事各自出一个,之后汇总由大家以无记名投票的方式选举出一个。本次丁健勇贡献的题目贴近生活,有趣味,难度适中。最后以较高票数中标。题目如下:[[BR]] {{{ 公司饭堂排队吃饭,有11元,13元,15元3个窗口。 固定有500人就餐,其中280人第一选择是11元窗口,150人第一选择是13元窗口,70人第一选择是15元窗口。 每分钟11元窗口可打饭3人,13元窗口可打饭2人,15元窗口可打饭1人,每分钟有10名员工依次进场。 由于人的排队耐心有限,所以假如11元窗口的队伍人数大于50人时,员工会选择13元窗口,当13元窗口的队伍人数大于30人时,员工会选择15元窗口, 当15元窗口人数大于15人时,员工则不会再等待,直接出外就餐。 求出饭堂一天的营业额。 }}} [[Image(kata1.png, 500px)]] 依靠小组成员的集体智慧与优秀的团队合作精神,经过2周的时间,成功地完成了这一题目。 [[BR]] 测试代码如下: {{{ package lunch; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import org.junit.After; import org.junit.AfterClass; import org.junit.Assert; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import static org.junit.Assert.*; /** * * @author pc */ public class LunchTest { private Lunch lunch = null; private LinkedList list; public LunchTest() { } @BeforeClass public static void setUpClass() { } @AfterClass public static void tearDownClass() { } @Before public void setUp() { lunch = new Lunch(); list = lunch.getPeople(); list.offer("11"); list.offer("13"); list.offer("11"); list.offer("11"); list.offer("15"); list.offer("15"); list.offer("11"); list.offer("13"); list.offer("13"); list.offer("13"); list.offer("13"); list.offer("13"); list.offer("13"); list.offer("13"); list.offer("15"); list.offer("15"); list.offer("11"); list.offer("13"); list.offer("13"); list.offer("13"); } @After public void tearDown() { } @Test public void testInit() { lunch = new Lunch(); lunch.init(); List people = lunch.getPeople(); Assert.assertEquals(Lunch.TOTAL_PEOPLE, lunch.getPeople().size()); int count11 = 0; int count13 = 0; int count15 = 0; for (String s : people) { if ("11".equals(s)) { count11++; } if ("13".equals(s)) { count13++; } if ("15".equals(s)) { count15++; } } //TODO 验证首选11元的人数 Assert.assertEquals(Lunch.TOTAL_PEOPLE_11, count11); //TODO 验证首选13元的人数 Assert.assertEquals(Lunch.TOTAL_PEOPLE_13, count13); //TODO 验证首选15元的人数 Assert.assertEquals(Lunch.TOTAL_PEOPLE_15, count15); //TODO 验证随机 } @Test public void testCheckpoint() { //lunch.init(); Map queue = lunch.getQueue(); queue.put("11", 0); queue.put("13", 0); queue.put("15", 0); lunch.setQueue(queue); lunch.checkpoint(); Assert.assertEquals(10, list.size()); Assert.assertEquals(1, (int) queue.get("11")); Assert.assertEquals(2, (int) queue.get("13")); Assert.assertEquals(1, (int) queue.get("15")); Assert.assertEquals(4, lunch.getPersonCount11()); Assert.assertEquals(4, lunch.getPersonCount13()); Assert.assertEquals(2, lunch.getPersonCount15()); lunch.checkpoint(); Assert.assertEquals(0, list.size()); Assert.assertEquals(0, (int) queue.get("11")); Assert.assertEquals(7, (int) queue.get("13")); Assert.assertEquals(2, (int) queue.get("15")); Assert.assertEquals(5, lunch.getPersonCount11()); Assert.assertEquals(11, lunch.getPersonCount13()); Assert.assertEquals(4, lunch.getPersonCount15()); } @Test public void testCheckPointHavePeople() { Map queue = lunch.getQueue(); queue.put("11", 50); queue.put("13", 30); queue.put("15", 10); lunch.setQueue(queue); lunch.checkpoint(); Assert.assertEquals(10, list.size()); Assert.assertEquals(47, (int) queue.get("11")); Assert.assertEquals(28, (int) queue.get("13")); Assert.assertEquals(14, (int) queue.get("15")); Assert.assertEquals(0, lunch.getPersonCount11()); Assert.assertEquals(0, lunch.getPersonCount13()); Assert.assertEquals(5, lunch.getPersonCount15()); } @Test public void testGetBalance() { int balance = lunch.getPersonCount11() * Lunch.COST_ELEVEN + lunch.getPersonCount13() * Lunch.COST_THIRTEEN + lunch.getPersonCount15() * Lunch.COST_FIFTEEN; Assert.assertEquals(balance, lunch.getBalance()); } @Test public void testChoose() { Map queue = new HashMap(); queue.put("11", 33); queue.put("13", 0); queue.put("15", 0); String people = "11"; lunch.setQueue(queue); Assert.assertTrue(lunch.choose11(people)); queue = new HashMap(); queue.put("11", 49); queue.put("13", 0); queue.put("15", 0); lunch.setQueue(queue); Assert.assertTrue(lunch.choose11(people)); queue = new HashMap(); queue.put("11", 50); queue.put("13", 0); queue.put("15", 0); lunch.setQueue(queue); Assert.assertFalse(lunch.choose11(people)); people = "13"; Assert.assertFalse(lunch.choose11(people)); } @Test public void testChoose13() { Map queue = new HashMap(); String people = "13"; queue = new HashMap(); queue.put("11", 0); queue.put("13", 30); queue.put("15", 0); lunch.setQueue(queue); Assert.assertFalse(lunch.choose13(people)); queue = new HashMap(); queue.put("11", 0); queue.put("13", 29); queue.put("15", 0); lunch.setQueue(queue); Assert.assertTrue(lunch.choose13(people)); people = "15"; Assert.assertFalse(lunch.choose13(people)); } @Test public void testChoose15() { Map queue = new HashMap(); String people = "15"; queue = new HashMap(); queue.put("11", 0); queue.put("13", 0); queue.put("15", 15); lunch.setQueue(queue); Assert.assertFalse(lunch.choose15(people)); queue = new HashMap(); queue.put("11", 0); queue.put("13", 0); queue.put("15", 14); lunch.setQueue(queue); Assert.assertTrue(lunch.choose15(people)); } @Test public void testHandlePieceOfLunch(){ Map queue = lunch.getQueue(); queue.put("11", 30); queue.put("13", 20); queue.put("15", 10); lunch.setQueue(queue); lunch.handlePieceOfLunch(); Assert.assertEquals(27, (int) queue.get("11")); Assert.assertEquals(18, (int) queue.get("13")); Assert.assertEquals(9, (int) queue.get("15")); queue = lunch.getQueue(); queue.put("11", 0); queue.put("13", 0); queue.put("15", 0); lunch.setQueue(queue); lunch.handlePieceOfLunch(); Assert.assertEquals(0, (int) queue.get("11")); Assert.assertEquals(0, (int) queue.get("13")); Assert.assertEquals(0, (int) queue.get("15")); queue = lunch.getQueue(); queue.put("11", 3); queue.put("13", 2); queue.put("15", 1); lunch.setQueue(queue); lunch.handlePieceOfLunch(); Assert.assertEquals(0, (int) queue.get("11")); Assert.assertEquals(0, (int) queue.get("13")); Assert.assertEquals(0, (int) queue.get("15")); } } }}} 源代码如下: {{{ package lunch; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Queue; /** * * @author pc */ public class Lunch { public static final int WAIT_NUM_OF_11 = 50; public static final int WAIT_NUM_OF_13 = 30; public static final int WAIT_NUM_OF_15 = 15; public static final int TOTAL_PEOPLE = 500; public static final int TOTAL_PEOPLE_11 = 280; public static final int TOTAL_PEOPLE_13 = 150; public static final int TOTAL_PEOPLE_15 = 70; public static final int COST_ELEVEN = 11; public static final int COST_THIRTEEN = 13; public static final int COST_FIFTEEN = 15; public static final int NUM_OF_HANDLE_11 = 3; public static final int NUM_OF_HANDLE_13 = 2; public static final int NUM_OF_HANDLE_15 = 1; private Map queue = new HashMap(); private LinkedList people = new LinkedList(); private int personCount11 = 0; private int personCount13 = 0; private int personCount15 = 0; public Map getQueue() { return queue; } public void setQueue(Map queue) { this.queue = queue; } public LinkedList getPeople() { return people; } public int getPersonCount11() { return personCount11; } public int getPersonCount13() { return personCount13; } public int getPersonCount15() { return personCount15; } /** * 初始化进场顺序 */ public void init() { for (int i = 0; i < TOTAL_PEOPLE_11; i++) { people.add("11"); } for (int i = 0; i < TOTAL_PEOPLE_13; i++) { people.add("13"); } for (int i = 0; i < TOTAL_PEOPLE_15; i++) { people.add("15"); } queue.put("11", 0); queue.put("13", 0); queue.put("15", 0); Collections.shuffle(people); } public int getBalance() { return COST_ELEVEN * personCount11 + COST_THIRTEEN * personCount13 + COST_FIFTEEN * personCount15; } /** * 每分钟时的处理方法 */ public void checkpoint() { for (int i = 0; i < 10; i++) { String p = people.poll(); // 判断队列人数 boolean result = choose11(p); if (result) { queue.put("11", queue.get("11") + 1); personCount11++; } else { result = choose13(p); if (result) { queue.put("13", queue.get("13") + 1); personCount13++; } else { if (choose15(p)) { queue.put("15", queue.get("15") + 1); personCount15++; } } } } handlePieceOfLunch(); } public void handlePieceOfLunch() { queue.put("11", queue.get("11") - 3 < 0 ? 0 : queue.get("11") - 3); queue.put("13", queue.get("13") - 2 < 0 ? 0 : queue.get("13") - 2); queue.put("15", queue.get("15") - 1 < 0 ? 0 : queue.get("15") - 1); } public boolean choose11(String p) { return queue.get("11") < WAIT_NUM_OF_11 && "11".equals(p); } public boolean choose13(String p) { return queue.get("13") < WAIT_NUM_OF_13 && ("11".equals(p) || "13".equals(p)); } public boolean choose15(String p) { return queue.get("15") < WAIT_NUM_OF_15; } /** * @param args the command line arguments */ public static void main(String[] args) { // TODO code application logic here int times = TOTAL_PEOPLE % 10 == 0 ? TOTAL_PEOPLE / 10 : TOTAL_PEOPLE / 10 + 1; Lunch lunch = new Lunch(); lunch.init(); for (int i = 0; i < times; i++) { lunch.checkpoint(); } int total = lunch.getBalance(); System.out.println("total:" + total); System.out.println("getPersonCount11:" + lunch.getPersonCount11()); System.out.println("getPersonCount13:" + lunch.getPersonCount13()); System.out.println("getPersonCount15:" + lunch.getPersonCount15()); } } }}} 这次Code Kata活动,总体来说,还是比较顺利,中途也出现了一些,代码设计上的分歧意见,和代码设计中发现的问题,但小组成员都虚心倾听别人意见,认真思考 最后讨论统一编程思路,大家都受益良多。[[BR]] 比如说,开始活动之前,王安宁就发现题目本身存在一些设计不合理的地方,后来经过讨论修改,敲订题目。[[BR]] 秦鸿源在大家在陷入过度设计陷井的时候,站出来,提出分解问题,各个击破的方案,让大家思路更清晰,更有条理。[[BR]] 陈阳提出使用队列比使用数组更适合解决打饭排队问题,丁健勇与邝巨桓,在小组成编码过程中给过很多非常不错的经验与建议。[[BR]] 新入职的黄志强和李炳岳,也作为顾问全程参与了活动,虽然没有直接参与编码,但也从中学习到了不少知识。[[BR]] 活动之中还有很多精彩的细节... 欢迎其他组有兴趣的同事加入:)