一个小例子看编程的扩展性

当码农已经20多年了,从上大学开始,被人面试了无数次,也面试了很多人。从2005年以后,我就开始面试别人,发现很多程序员编程的时候,不会考虑编程的扩展性和执行效率。我自己出了一个简单的面试题,居然很多人没有给出我想要的答案,题目如下,如果你想测试你自己的能力,你也可以做一下,但千万不要看我下面的答案哟。

字符串
1.  A1,B1,C1,C2,B2,A2,D1,D2,D3
2.  E1,E2,E3,F1,F2,F4

字符串类型特特点:字符串里面的每个TOKEN都是 字母+数字,需要对字符串做做一个函数处理得到下面结果
题中只给了两个符合上面规则的字符串,符合上面规则的字符串是无限的。

字符串1结果:
A -->{A1,A2}
B -->{B1,B2}
C -->{C1,C2}
D-->{D1,D2,D3}

字符串2结果:
E -->{ E1,E2,E3}
F -->{F1,F2,F3}

请写一个函数, 输入是字符串,输出打印结果如上图所示
这是一个简单的算法题,你请做一下。

 

下面是一个在淘宝手机测试团队工作过的哥们儿的答案,运行出了正确的结果,但不是我想要的。(没有贬低淘宝手机团队的意思,只是想说明这种事情在国内很普遍,一流的大公司也难避免,这哥们儿入职以后能力还是很强的)

public class UpdateTest {

    privatestatic String str1 = "A1,B1,C1,C2,B2,A2,D1,D2,D3";

    privatestatic String str2 = "E1,E2,E3,F1,F2,F4";

 

    public staticvoid main(String args[]){

       System.out.print(getFormatStr(str1));

       System.out.print(getFormatStr(str2));

    }

    /**

     * 提取字符串

     * @param str

     * @return

     */

    privatestatic String getFormatStr(String str){

        String[]strs = str.split(",");

       List<String> stringList = Arrays.asList(strs);

        StringsingalStr = "";

        StringsingalStr2 = "";

        char c ;

       StringBuilder stringBuilder = new StringBuilder();

       List<String>rmList = new ArrayList<>(20);

        for (inti=0 ; i< stringList.size(); i++){

           singalStr = stringList.get(i);

            if(rmList.contains(singalStr)){

               continue;

            }

            c =singalStr.charAt(0);

            stringBuilder.append(c).append("--->{").append(singalStr);

 

           //handle之后的数据

            for(int j = i+1; j < stringList.size(); j++){

               singalStr2 = stringList.get(j);

               if (rmList.contains(singalStr2))continue;

                if (singalStr2.charAt(0) == c){

                   stringBuilder.append(",").append(singalStr2);

                   rmList.add(singalStr2);

                }

            }

           stringBuilder.append("} \n");

        }

        return stringBuilder.toString();

    }

}

运行结果:


这个答案有下面不足的地方:

1.    实现过于复杂,FOR循环层层套,可读性很差。

2.    局部变量很多,看上去很凌乱的感觉。

3.    循环次数太多,效率不高,字符串进行很很多次遍历。

4.    扩展性很差,如果我改变了分组条件,比如使用前面两个字符做作为分组条件,就需要去修改。

5.    其实打印环节是不需要的,我只是表达分组后的数据结构的样子,没有完全理解我的题意。

 

下面是一个做IOS的哥们儿给我的答案,要简练很多,后来在公司的表现也很出色,基本上达到了我的要求。


他使用了MAP数据结构来完成分组工作,没有从新发明轮子,而且巧妙的使用了已经有的数据结构来解决问题,而且准确的理解了我的题意。而且,代码的效率很高,只执行了一次遍历,在数据量大的情况下,优势会体现出来。

 

下面是另外一个面试者的答案,比起没有做出这道题的应试者已经强了,但我们没有录取,因为这个实现扩展性太差了。

 

      publicstatic void prcessString(String str){

           ListlistA=new ArrayList();

           ListlistB=new ArrayList();

           ListlistC=new ArrayList();

           ListlistD=new ArrayList();

           ListlistE=new ArrayList();

           ListlistF=new ArrayList();

           String[]strs=str.split(",");

           for(inti=0;i<strs.length;i++){

              String s=strs[i];

              char c=s.charAt(0);

              if(c=='A'){

                    listA.add(s);

              }else if(c=='B'){

                    listB.add(s);

              }else if(c=='C'){

                    listC.add(s);

              }else if(c=='D'){

                    listD.add(s);

              }else if(c=='E'){

                    listE.add(s);

              }else{

                    listF.add(s);

              }

           }

      }

且不说,我把条件换成前面两个字母,就是我多加一个字母G,比如字符串G1,G2,这个办法就无法正确分组,他的答案只是针对我的例子做了解答。

 

这道题的本质就是一个分组问题,分组在软件开发当中应用的场景很多。用过SQL的人都知道,SQL里面有GROUP BY的功能,就是一个分组汇总问题。当前非常火的大数据技术Hadoop,里面的Map-Reduce计算框架,也有分组问题。例如,基于Hadoop的HIVE数据库,它支持GROUP BY操作,底层实现就是调用了Map-Reduce的分组功能。在这个分组题目当中,我简化了分组问题,把分组的键值定义为字符串的第一个字母,实际上分组的时候键值形式是可以多种多样的。优秀的程序员需要有广泛的知识,要多思考各自使用场景,能够把这些场景串联起来,这样在编程的时候,就会考虑的更全面,程序的扩展性也就更强。大数据这些都有些高大上,其实这种例子在我们日常使用的类库里面就可以发现,如下面这个例子:

 

Collections.sort(aList);

Collections.sort(aList, aCompare);

Collections.shuffle(list);

Collections.shuffle(list, random);

 

为什么JAVA的Collections里面都提供了多种实现呢?这就是类库设计者给我们提供的扩展性,设计的基本原则是:使用起步简单,因为提供缺省实现;如果缺省实现不能满足要求,用户可以方便的进行客户化。通俗些说,就是老少皆宜。

 

这个题目当中,虽然我只是告诉你使用第一个字母作为键值,但我希望面试者能够深入问我,这个函数是否需要扩展,并且给出我扩展的方案。如果这个道题按照JAVA Collections的思路来做,我们应该先设计函数方法。

 

MyUtil.groupBy(String string,int key);

MyUtil.groupBy(String string,KeyCreator c);

 

第一个函数提供了缺省的键值实现,用户可以自己决定使用前N个字符作为键值,第二个函数提供了更大的扩展,用户可以自己决定怎么来生成这个键值,两个函数除了键值生成部分不同,分组的过程是完全一样的,可以把分组过程做一个PRIVATE函数统一处理:

 

private Map doGroupBy(String str,int keyLen,KeyCreator c){

   String[]tokens=str.split(",");

   Map result=new HashMap();

   for(inti=0;i<tokens;i++){

     String t=tokens[i];

     Object key=null;

     if(null!=c){

       key=c.createKey(t);

     }else{

      key=t.subString(0,keyLen);

     }

     List l=result.get(key);

     if(null==l){

       l=new ArrayList();

       result.put(key,l);

     }

     l.add(t);

   }

   return result;

}

 

这里使用了很少的局部变量,而且放在了合适的作用域当中,流程会显得很清晰,可读性很强,执行效率高,扩展性强。至于参数检验,你可以加入,也可以不加入,如果不加入在函数注释讲解清楚也是可以的,这样调用者自己调用之前会保证参数的合规性。

 

这是一个非常简单的例子,其实在做程序的时候,我们有非常非常多的地方都是需要仔细思考的,编程的功力都是在这种小细节当中体现出来的。优秀的代码就像一段美妙的音乐,让人感觉心旷神怡。优秀的代码的套路都是相似的,通过阅读优秀的开源代码,JAVA源代码,分析当中的设计道理,发现规律,总结经验,自己也是可以写出优美的代码的。

 

美国的计算机课程非常重视算法和数据结构的学习,而且学习这些理论的时候,还会加入实践的部分,如对MINUX的学习,这是一个教学版LINUX,里面有很多算法和数据结构的应用案例。通过理论+实践的学习,学生能够深入的理解算法和数据结构的魅力。在工作当中,我发现很多程序员都不知道巧妙的使用数据结构来简化程序,程序虽然实现了功能,但可读性扩展性却很差。

 

程序扩展性不强,可读写性差,执行效率差,都直接影响软件的开发成本和质量。可读性差,会提升程序员的协作成本。我常听的一句话就是,’这部分功能不是我写的,是他写的,还是让他来修改吧,他的代码我看懂费时间’,这常常导致任务堆积到几个人身上,产生任务分配不均衡的情况,如果强迫其他人接受,会导致很多BUG的产生。扩展性不强也会导致成本的上升,我们知道很多时候需求是变化的,即使非常有经验的产品经理设计产品,也是会有些问题考虑不到的,这时候如果程序员具有产品思维,协助产品经理发现问题,提示产品经理问题,那么问题就会在程序开发早期发现。具有产品思维的程序员做程序都会考虑这个需求的后期可能的变化,再变成的时候就把这种变化性考虑进去了,后期程序的修改成本会大大降低。执行效率很显然会影响产品质量,很多程序员开发的时候数据量不大,他不会感觉到问题,这些问题都是在产品开发后期,甚至到产品应用以后才发现的,发现的越晚,那么付出的成本就越高。

 

造成这种情况发生的原因,其实和公司管理也分不开的,很多公司老板对员工技能培训特别不重视,现在国内一线公司这方面还做的不错,有技术交流会,但绝大多数公司都在赶项目,赶进度。中国有句老话,磨刀不费砍材功,做事情不是花时间越多越能做好,效率是非常重要的,给员工提升培训,会大大提高团队的开发效率和产品质量,可以帮助团队进入一个良性循环。国内很多团队都是恶心循环,技能低,代码质量低,出问题多,问题多,就需要多加班加点,没有时间进行技能培训和提升,一直处于一种低效率循环当中。我遇到很多工作了5年以上的程序员,问一些执行效率和数据结构的问题,都全然不知,代码风格也非常乱,写只有自己能看懂的代码,其实过一个星期以后,他自己都看不大懂了。

 

产品的质量来源于每个开发人员的技能水平,写这边文章就是想让更多的人重视程序员的素质培养。优秀的程序员需要有服务客户的心态,使用你写的类库的其他程序员就是你的客户,以后接收你的程序代码的程序员,就是你的客户,你需要尽量为客户带来方便。


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值