Changes between Version 1 and Version 2 of codekata/lunchRefactor


Ignore:
Timestamp:
07/01/2013 09:41:27 AM (13 years ago)
Author:
chenyang
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • codekata/lunchRefactor

    v1 v2  
    11'''6.21,6.28 重构lunch代码道场记实''' 
     2 
     3[[BR]] 
     4 
     5代码道场的参与者:秦鸿源,陈阳,王安宁,丁健勇,邝巨恒,李炳, 李峰 
     6 
     7[[BR]] 
     8 
     9地点:4G会议室  
     10 
    211[[BR]] 
    312    论坛小组在5.10,5.17进行了对命名为lunch的题目对进CodeKata练习,活动效果不错,小组成员普遍觉得受匪浅.[[BR]] 
     
    918        很明显,目前的代码,是困难的,代码中有太多的难以维护的变量,和魔术数字,新开一个打饭窗口,需要修改大量的代码。 
    1019[[BR]] 
    11         这次的重构活动,大家也是摸着石头过河,丁健勇,秦鸿源首先对有着相似的逻辑代码,进行了小规模的封装,邝巨桓提出用数组存储各种打饭的类型,这样可以用数组来遍历,避免硬代码的出现,重构后,效果还不错。[[BR]] 
    12         最后,陈阳提出,用枚举来代替数组,有着更好的可维护性与可读性。我们新增了LunchType.java这个枚举类,来封装打饭的类型。 
     20        这次的重构活动,大家也是摸着石头过河,丁健勇,秦鸿源首先对有着相似的逻辑代码,进行了小规模的封装,邝巨桓提出用数组存储各种打饭的类型,这样可以用数组来遍历,避免硬代码的出现。[[BR]] 
     21陈阳提出,用枚举来代替数组,有着更好的可维护性与可读性.我们新增了LunchType.java这个枚举类,来封装打饭的类型。 
     22{{{ 
     23package lunch; 
     24 
     25/** 
     26 * 
     27 * @author pc 
     28 */ 
     29public 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{{{ 
     84package lunch; 
     85 
     86import java.util.Collections; 
     87import java.util.HashMap; 
     88import java.util.LinkedList; 
     89import java.util.Map; 
     90 
     91/** 
     92 * 
     93 * @author pc 
     94 */ 
     95public 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]] 
    13267{{{ 
    14268/* 
     
    18272package lunch; 
    19273 
     274import java.util.HashMap; 
     275import java.util.LinkedList; 
     276import java.util.List; 
     277import java.util.Map; 
     278import org.junit.After; 
     279import org.junit.AfterClass; 
     280import org.junit.Assert; 
     281import org.junit.Before; 
     282import org.junit.BeforeClass; 
     283import org.junit.Test; 
     284import static org.junit.Assert.*; 
     285 
    20286/** 
    21287 * 
    22288 * @author pc 
    23289 */ 
    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; 
     290public 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();        
    47381        for(LunchType lunchType : LunchType.values()){ 
    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    } 
    69483} 
    70484         
    71485}}} 
    72  
    73  
    74  
    75  
    76  
     486   此次的重构,基本上满足了我们的活动目的,但也有一些不够完美的地方.邝巨桓提议说: 
     487我们重构,好像就是在用枚举替代数组,并没有让大家学习到更多的知识。[[BR]] 
     488由于时间的原因,我们这期的活动,只能做这么多. 
     489如果时间充裕,我们可以再做一次大型的重构,比如说,我们的题目,是一个典型的生产者-消费者模式,[[BR]] 
     490我们可以把程序设计成多线程模式,让职工进入食堂排队,和阿姨打饭同时进行.[[BR]] 
     491 
     492再说个小福利,前几次代码道场活动,都是用的笔记本电脑的的键盘,大家都反应很难用,最后,我们借用了李炳岳的usb接口健盘。