| 11 | | 这次的重构活动,大家也是摸着石头过河,丁健勇,秦鸿源首先对有着相似的逻辑代码,进行了小规模的封装,邝巨桓提出用数组存储各种打饭的类型,这样可以用数组来遍历,避免硬代码的出现,重构后,效果还不错。[[BR]] |
| 12 | | 最后,陈阳提出,用枚举来代替数组,有着更好的可维护性与可读性。我们新增了LunchType.java这个枚举类,来封装打饭的类型。 |
| | 20 | 这次的重构活动,大家也是摸着石头过河,丁健勇,秦鸿源首先对有着相似的逻辑代码,进行了小规模的封装,邝巨桓提出用数组存储各种打饭的类型,这样可以用数组来遍历,避免硬代码的出现。[[BR]] |
| | 21 | 陈阳提出,用枚举来代替数组,有着更好的可维护性与可读性.我们新增了LunchType.java这个枚举类,来封装打饭的类型。 |
| | 22 | {{{ |
| | 23 | package lunch; |
| | 24 | |
| | 25 | /** |
| | 26 | * |
| | 27 | * @author pc |
| | 28 | */ |
| | 29 | public enum LunchType { |
| | 30 | |
| | 31 | Type5(5, 100, 300, 5), |
| | 32 | Type11(11, 50, 280, 3), |
| | 33 | Type13(13, 30, 150, 2), |
| | 34 | Type15(15, 15, 70, 1), |
| | 35 | Type20(20, 10, 20, 1); |
| | 36 | |
| | 37 | private int value; |
| | 38 | private int waitNum; |
| | 39 | private int totalPeople; |
| | 40 | private int handleNum; |
| | 41 | |
| | 42 | private LunchType(int value, int waitNum, int totalPeople, int handleNum){ |
| | 43 | this.value = value; |
| | 44 | this.waitNum = waitNum; |
| | 45 | this.totalPeople = totalPeople; |
| | 46 | this.handleNum = handleNum; |
| | 47 | |
| | 48 | } |
| | 49 | |
| | 50 | public static int countPepple(){ |
| | 51 | int sum = 0; |
| | 52 | for(LunchType lunchType : LunchType.values()){ |
| | 53 | sum += lunchType.getTotalPeople(); |
| | 54 | } |
| | 55 | return sum; |
| | 56 | } |
| | 57 | |
| | 58 | public int getValue() { |
| | 59 | return value; |
| | 60 | } |
| | 61 | |
| | 62 | public int getWaitNum() { |
| | 63 | return waitNum; |
| | 64 | } |
| | 65 | |
| | 66 | public int getTotalPeople() { |
| | 67 | return totalPeople; |
| | 68 | } |
| | 69 | |
| | 70 | public int getHandleNum() { |
| | 71 | return handleNum; |
| | 72 | } |
| | 73 | |
| | 74 | } |
| | 75 | |
| | 76 | }}} |
| | 77 | 使用枚举,将原先设计的那些松散的常量,套餐价格,每种套餐的最大排队人数,每种套餐的第一志愿人数,打饭阿姨每分钟打饭数,聚合在一起。 |
| | 78 | [[BR]] |
| | 79 | 使得新开一个打饭窗口变得更容易,同时,枚举也支持遍历,让代码变得更灵活 |
| | 80 | [[BR]] |
| | 81 | 引入LunchType后我们大刀阔斧地对主代码Lunch.java进行了重构 |
| | 82 | [[BR]] |
| | 83 | {{{ |
| | 84 | package lunch; |
| | 85 | |
| | 86 | import java.util.Collections; |
| | 87 | import java.util.HashMap; |
| | 88 | import java.util.LinkedList; |
| | 89 | import java.util.Map; |
| | 90 | |
| | 91 | /** |
| | 92 | * |
| | 93 | * @author pc |
| | 94 | */ |
| | 95 | public class Lunch { |
| | 96 | |
| | 97 | private Map<LunchType, Integer> queue = new HashMap<LunchType, Integer>(); |
| | 98 | private LinkedList<LunchType> people = new LinkedList<LunchType>(); |
| | 99 | private Map<LunchType, Integer> personCounts = new HashMap<LunchType, Integer>(); |
| | 100 | |
| | 101 | public Map<LunchType, Integer> getQueue() { |
| | 102 | return queue; |
| | 103 | } |
| | 104 | |
| | 105 | public void setPersonCounts(Map<LunchType, Integer> personCounts) { |
| | 106 | this.personCounts = personCounts; |
| | 107 | } |
| | 108 | |
| | 109 | public Map<LunchType, Integer> getPersonCounts() { |
| | 110 | return personCounts; |
| | 111 | } |
| | 112 | |
| | 113 | public void setQueue(Map<LunchType, Integer> queue) { |
| | 114 | this.queue = queue; |
| | 115 | } |
| | 116 | |
| | 117 | public LinkedList<LunchType> getPeople() { |
| | 118 | return people; |
| | 119 | } |
| | 120 | |
| | 121 | public int getPersonCountByPrice(LunchType type){ |
| | 122 | return personCounts.get(type); |
| | 123 | } |
| | 124 | |
| | 125 | |
| | 126 | /** |
| | 127 | * 初始化进场顺序 |
| | 128 | */ |
| | 129 | public void init() { |
| | 130 | |
| | 131 | for(LunchType lunchType : LunchType.values()){ |
| | 132 | for (int i = 0; i < lunchType.getTotalPeople(); i++) { |
| | 133 | people.add(lunchType); |
| | 134 | } |
| | 135 | queue.put(lunchType, 0); |
| | 136 | personCounts.put(lunchType, 0); |
| | 137 | } |
| | 138 | |
| | 139 | Collections.shuffle(people); |
| | 140 | } |
| | 141 | |
| | 142 | public int getBalance() { |
| | 143 | int balance = 0; |
| | 144 | for(LunchType type : LunchType.values()){ |
| | 145 | balance += personCounts.get(type) * type.getValue(); |
| | 146 | } |
| | 147 | |
| | 148 | return balance; |
| | 149 | } |
| | 150 | |
| | 151 | /** |
| | 152 | * 每分钟时的处理方法 |
| | 153 | */ |
| | 154 | public void checkpoint() { |
| | 155 | out: for (int i = 0; i < 15; i++) { |
| | 156 | if (!people.isEmpty()) { |
| | 157 | LunchType p = people.poll(); |
| | 158 | |
| | 159 | for (LunchType type : LunchType.values()) { |
| | 160 | if (type.ordinal() == 0) { |
| | 161 | if (queueIsNotFull(type) && type == p) { |
| | 162 | queue.put(type, queue.get(type) + 1); |
| | 163 | personCounts.put(type, personCounts.get(type)+1); |
| | 164 | continue out; |
| | 165 | } |
| | 166 | } |
| | 167 | |
| | 168 | if (type.ordinal() > 0 && type.ordinal() < LunchType.values().length - 1) { |
| | 169 | if (queueIsNotFull(type) && type.getValue() >= p.getValue()) { |
| | 170 | queue.put(type, queue.get(type) + 1); |
| | 171 | personCounts.put(type, personCounts.get(type)+1); |
| | 172 | continue out; |
| | 173 | } |
| | 174 | } |
| | 175 | |
| | 176 | if (type.ordinal() == LunchType.values().length - 1) { |
| | 177 | if (queueIsNotFull(type)) { |
| | 178 | queue.put(type, queue.get(type) + 1); |
| | 179 | personCounts.put(type, personCounts.get(type)+1); |
| | 180 | continue out; |
| | 181 | } |
| | 182 | } |
| | 183 | } |
| | 184 | } |
| | 185 | } |
| | 186 | |
| | 187 | handlePieceOfLunch(); |
| | 188 | } |
| | 189 | |
| | 190 | public void handlePieceOfLunch() { |
| | 191 | |
| | 192 | |
| | 193 | for(LunchType type : LunchType.values()){ |
| | 194 | handleQueue(type); |
| | 195 | } |
| | 196 | |
| | 197 | } |
| | 198 | |
| | 199 | public boolean queueIsNotFull(LunchType lunchType){ |
| | 200 | return queue.get( |
| | 201 | lunchType) < lunchType.getWaitNum(); |
| | 202 | } |
| | 203 | |
| | 204 | /** |
| | 205 | * @param args the command line arguments |
| | 206 | */ |
| | 207 | public static void main(String[] args) { |
| | 208 | // TODO code application logic here |
| | 209 | int times = LunchType.countPepple() % 10 == 0 ? |
| | 210 | LunchType.countPepple() / 10 : LunchType.countPepple() / 10 + 1; |
| | 211 | Lunch lunch = new Lunch(); |
| | 212 | lunch.init(); |
| | 213 | for (int i = 0; i < times; i++) { |
| | 214 | lunch.checkpoint(); |
| | 215 | } |
| | 216 | int total = lunch.getBalance(); |
| | 217 | System.out.println("total:" + total); |
| | 218 | for(LunchType type : LunchType.values()){ |
| | 219 | System.out.println("getPersonCount"+type.getValue() +":" + lunch.getPersonCountByPrice(type)); |
| | 220 | } |
| | 221 | } |
| | 222 | |
| | 223 | private void handleQueue(LunchType type) { |
| | 224 | queue.put(type, queue.get(type) - type.getHandleNum() < 0 |
| | 225 | ? 0 : queue.get(type) - type.getHandleNum()); |
| | 226 | } |
| | 227 | } |
| | 228 | |
| | 229 | }}} |
| | 230 | 可以看到代码比上一版,干净了许多. |
| | 231 | 这里有一个小插曲,在代码道场快要结束的时候,第一版中有一段处理打饭业务逻辑的代码,如下:[[BR]] |
| | 232 | {{{ |
| | 233 | ... |
| | 234 | for (int i = 0; i < 10; i++) { |
| | 235 | String p = people.poll(); |
| | 236 | |
| | 237 | // 判断队列人数 |
| | 238 | boolean result = choose11(p); |
| | 239 | if (result) { |
| | 240 | queue.put("11", queue.get("11") + 1); |
| | 241 | personCount11++; |
| | 242 | } else { |
| | 243 | result = choose13(p); |
| | 244 | if (result) { |
| | 245 | queue.put("13", queue.get("13") + 1); |
| | 246 | personCount13++; |
| | 247 | } else { |
| | 248 | if (choose15(p)) { |
| | 249 | queue.put("15", queue.get("15") + 1); |
| | 250 | personCount15++; |
| | 251 | } |
| | 252 | } |
| | 253 | } |
| | 254 | } |
| | 255 | |
| | 256 | handlePieceOfLunch(); |
| | 257 | |
| | 258 | ... |
| | 259 | }}} |
| | 260 | 这段代码是硬代码,不易维护,不易扩展,起初大家也认为这个地方,要让它"活"起来,有难度。[[BR]] |
| | 261 | 但大家一讨论,枚举可以遍历,values() 可以 得到数据, 而且枚举的ordinal(),方法可以知道当前枚举的位置。[[BR]] |
| | 262 | 可以尝试一下,由邝巨恒编码,陈阳,丁健勇,王安宁等同事,协助编码,活动达到高潮[[BR]] |
| | 263 | 最后,成功逆袭,拿下了它。 |
| | 264 | [[BR]] |
| | 265 | 当然,我们的重构是建立在测试驱动的基础之上的,单元测试代码如下。 |
| | 266 | [[BR]] |
| 24 | | public enum LunchType { |
| 25 | | |
| 26 | | Type5(5, 100, 300, 5), |
| 27 | | Type11(11, 50, 280, 3), |
| 28 | | Type13(13, 30, 150, 2), |
| 29 | | Type15(15, 15, 70, 1), |
| 30 | | Type20(20, 10, 20, 1); |
| 31 | | |
| 32 | | private int value; |
| 33 | | private int waitNum; |
| 34 | | private int totalPeople; |
| 35 | | private int handleNum; |
| 36 | | |
| 37 | | private LunchType(int value, int waitNum, int totalPeople, int handleNum){ |
| 38 | | this.value = value; |
| 39 | | this.waitNum = waitNum; |
| 40 | | this.totalPeople = totalPeople; |
| 41 | | this.handleNum = handleNum; |
| 42 | | |
| 43 | | } |
| 44 | | |
| 45 | | public static int countPepple(){ |
| 46 | | int sum = 0; |
| | 290 | public class LunchTest { |
| | 291 | |
| | 292 | private Lunch lunch = null; |
| | 293 | private LinkedList<LunchType> list; |
| | 294 | |
| | 295 | public LunchTest() { |
| | 296 | } |
| | 297 | |
| | 298 | @BeforeClass |
| | 299 | public static void setUpClass() { |
| | 300 | } |
| | 301 | |
| | 302 | @AfterClass |
| | 303 | public static void tearDownClass() { |
| | 304 | } |
| | 305 | |
| | 306 | @Before |
| | 307 | public void setUp() { |
| | 308 | lunch = new Lunch(); |
| | 309 | list = lunch.getPeople(); |
| | 310 | list.offer(LunchType.Type11); |
| | 311 | list.offer(LunchType.Type13); |
| | 312 | list.offer(LunchType.Type11); |
| | 313 | list.offer(LunchType.Type11); |
| | 314 | list.offer(LunchType.Type15); |
| | 315 | list.offer(LunchType.Type15); |
| | 316 | list.offer(LunchType.Type11); |
| | 317 | list.offer(LunchType.Type13); |
| | 318 | list.offer(LunchType.Type13); |
| | 319 | list.offer(LunchType.Type13); |
| | 320 | |
| | 321 | list.offer(LunchType.Type13); |
| | 322 | list.offer(LunchType.Type13); |
| | 323 | list.offer(LunchType.Type13); |
| | 324 | list.offer(LunchType.Type13); |
| | 325 | list.offer(LunchType.Type15); |
| | 326 | list.offer(LunchType.Type15); |
| | 327 | list.offer(LunchType.Type11); |
| | 328 | list.offer(LunchType.Type13); |
| | 329 | list.offer(LunchType.Type13); |
| | 330 | list.offer(LunchType.Type13); |
| | 331 | } |
| | 332 | |
| | 333 | @After |
| | 334 | public void tearDown() { |
| | 335 | } |
| | 336 | |
| | 337 | |
| | 338 | @Test |
| | 339 | public void testInit() { |
| | 340 | lunch = new Lunch(); |
| | 341 | lunch.init(); |
| | 342 | List<LunchType> people = lunch.getPeople(); |
| | 343 | Assert.assertEquals(LunchType.countPepple(), lunch.getPeople().size()); |
| | 344 | int count11 = 0; |
| | 345 | int count13 = 0; |
| | 346 | int count15 = 0; |
| | 347 | for (LunchType s : people) { |
| | 348 | switch(s){ |
| | 349 | case Type11: |
| | 350 | count11++; |
| | 351 | break; |
| | 352 | case Type13: |
| | 353 | count13++; |
| | 354 | break; |
| | 355 | case Type15: |
| | 356 | count15++; |
| | 357 | break; |
| | 358 | } |
| | 359 | |
| | 360 | } |
| | 361 | //TODO 验证首选11元的人数 |
| | 362 | Assert.assertEquals(LunchType.Type11.getTotalPeople(), count11); |
| | 363 | |
| | 364 | //TODO 验证首选13元的人数 |
| | 365 | Assert.assertEquals(LunchType.Type13.getTotalPeople(), count13); |
| | 366 | |
| | 367 | //TODO 验证首选15元的人数 |
| | 368 | Assert.assertEquals(LunchType.Type15.getTotalPeople(), count15); |
| | 369 | |
| | 370 | //TODO 验证随机 |
| | 371 | |
| | 372 | } |
| | 373 | |
| | 374 | @Test |
| | 375 | public void testCheckpoint() { |
| | 376 | //lunch.init(); |
| | 377 | |
| | 378 | |
| | 379 | Map<LunchType, Integer> queue = lunch.getQueue(); |
| | 380 | Map<LunchType, Integer> personCount = lunch.getPersonCounts(); |
| 48 | | sum += lunchType.getTotalPeople(); |
| 49 | | } |
| 50 | | return sum; |
| 51 | | } |
| 52 | | |
| 53 | | public int getValue() { |
| 54 | | return value; |
| 55 | | } |
| 56 | | |
| 57 | | public int getWaitNum() { |
| 58 | | return waitNum; |
| 59 | | } |
| 60 | | |
| 61 | | public int getTotalPeople() { |
| 62 | | return totalPeople; |
| 63 | | } |
| 64 | | |
| 65 | | public int getHandleNum() { |
| 66 | | return handleNum; |
| 67 | | } |
| 68 | | |
| | 382 | queue.put(lunchType, 0); |
| | 383 | personCount.put(lunchType, 0); |
| | 384 | } |
| | 385 | |
| | 386 | lunch.checkpoint(); |
| | 387 | Assert.assertEquals(10, list.size()); |
| | 388 | Assert.assertEquals(1, (int) queue.get(LunchType.Type11)); |
| | 389 | Assert.assertEquals(2, (int) queue.get(LunchType.Type13)); |
| | 390 | Assert.assertEquals(1, (int) queue.get(LunchType.Type15)); |
| | 391 | Assert.assertEquals(4, lunch.getPersonCountByPrice(LunchType.Type11)); |
| | 392 | Assert.assertEquals(4, lunch.getPersonCountByPrice(LunchType.Type13)); |
| | 393 | Assert.assertEquals(2, lunch.getPersonCountByPrice(LunchType.Type15)); |
| | 394 | |
| | 395 | lunch.checkpoint(); |
| | 396 | Assert.assertEquals(0, list.size()); |
| | 397 | Assert.assertEquals(0, (int) queue.get(LunchType.Type11)); |
| | 398 | Assert.assertEquals(7, (int) queue.get(LunchType.Type13)); |
| | 399 | Assert.assertEquals(2, (int) queue.get(LunchType.Type15)); |
| | 400 | Assert.assertEquals(5, lunch.getPersonCountByPrice(LunchType.Type11)); |
| | 401 | Assert.assertEquals(11, lunch.getPersonCountByPrice(LunchType.Type13)); |
| | 402 | Assert.assertEquals(4, lunch.getPersonCountByPrice(LunchType.Type15)); |
| | 403 | } |
| | 404 | |
| | 405 | @Test |
| | 406 | public void testCheckPointHavePeople() { |
| | 407 | Map<LunchType, Integer> queue = lunch.getQueue(); |
| | 408 | Map<LunchType, Integer> personCount = lunch.getPersonCounts(); |
| | 409 | |
| | 410 | for(LunchType type:LunchType.values()){ |
| | 411 | queue.put(type, 0); |
| | 412 | personCount.put(type, 0); |
| | 413 | } |
| | 414 | |
| | 415 | queue.put(LunchType.Type11, 50); |
| | 416 | queue.put(LunchType.Type13, 30); |
| | 417 | queue.put(LunchType.Type15, 10); |
| | 418 | queue.put(LunchType.Type20, 5); |
| | 419 | |
| | 420 | lunch.setQueue(queue); |
| | 421 | |
| | 422 | lunch.checkpoint(); |
| | 423 | Assert.assertEquals(10, list.size()); |
| | 424 | Assert.assertEquals(47, (int) queue.get(LunchType.Type11)); |
| | 425 | Assert.assertEquals(28, (int) queue.get(LunchType.Type13)); |
| | 426 | Assert.assertEquals(14, (int) queue.get(LunchType.Type15)); |
| | 427 | Assert.assertEquals(0, lunch.getPersonCountByPrice(LunchType.Type11)); |
| | 428 | Assert.assertEquals(0, lunch.getPersonCountByPrice(LunchType.Type13)); |
| | 429 | Assert.assertEquals(5, lunch.getPersonCountByPrice(LunchType.Type15)); |
| | 430 | } |
| | 431 | |
| | 432 | |
| | 433 | @Test |
| | 434 | public void testHandlePieceOfLunch(){ |
| | 435 | Map<LunchType, Integer> queue = lunch.getQueue(); |
| | 436 | for(LunchType type:LunchType.values()){ |
| | 437 | queue.put(type, 0); |
| | 438 | } |
| | 439 | queue.put(LunchType.Type11, 30); |
| | 440 | queue.put(LunchType.Type13, 20); |
| | 441 | queue.put(LunchType.Type15, 10); |
| | 442 | lunch.setQueue(queue); |
| | 443 | |
| | 444 | lunch.handlePieceOfLunch(); |
| | 445 | Assert.assertEquals(27, (int) queue.get(LunchType.Type11)); |
| | 446 | Assert.assertEquals(18, (int) queue.get(LunchType.Type13)); |
| | 447 | Assert.assertEquals(9, (int) queue.get(LunchType.Type15)); |
| | 448 | |
| | 449 | queue = lunch.getQueue(); |
| | 450 | queue.put(LunchType.Type11, 0); |
| | 451 | queue.put(LunchType.Type13, 0); |
| | 452 | queue.put(LunchType.Type15, 0); |
| | 453 | lunch.setQueue(queue); |
| | 454 | lunch.handlePieceOfLunch(); |
| | 455 | Assert.assertEquals(0, (int) queue.get(LunchType.Type11)); |
| | 456 | Assert.assertEquals(0, (int) queue.get(LunchType.Type13)); |
| | 457 | Assert.assertEquals(0, (int) queue.get(LunchType.Type15)); |
| | 458 | |
| | 459 | queue = lunch.getQueue(); |
| | 460 | queue.put(LunchType.Type11, 3); |
| | 461 | queue.put(LunchType.Type13, 2); |
| | 462 | queue.put(LunchType.Type15, 1); |
| | 463 | lunch.setQueue(queue); |
| | 464 | lunch.handlePieceOfLunch(); |
| | 465 | Assert.assertEquals(0, (int) queue.get(LunchType.Type11)); |
| | 466 | Assert.assertEquals(0, (int) queue.get(LunchType.Type13)); |
| | 467 | Assert.assertEquals(0, (int) queue.get(LunchType.Type15)); |
| | 468 | |
| | 469 | } |
| | 470 | |
| | 471 | @Test |
| | 472 | public void testQueueIsNotFull() { |
| | 473 | Lunch lunch = new Lunch(); |
| | 474 | Map<LunchType, Integer> queue = new HashMap(); |
| | 475 | queue.put(LunchType.Type11, 30); |
| | 476 | queue.put(LunchType.Type13, 20); |
| | 477 | queue.put(LunchType.Type15, 10); |
| | 478 | |
| | 479 | lunch.setQueue(queue); |
| | 480 | Assert.assertTrue(lunch.queueIsNotFull(LunchType.Type13)); |
| | 481 | |
| | 482 | } |