美文网首页互联网科技程序员
优雅编程之这样编写方法,你就“正常”了(二十八)

优雅编程之这样编写方法,你就“正常”了(二十八)

作者: 阿_毅 | 来源:发表于2016-10-06 15:48 被阅读280次

    开心一笑

    【一大哥去医院看病。
    医生问:你得了什么病?
    大哥说: 我得了间接性失忆症。
    医生问:具体什么症状?
    大哥说:我一看到漂亮的姑娘就忘记自己已结婚了。
    医生说:滚滚滚,这病我自己都没治好!】

    提出问题

    项目开发中,使用方法要注意的一些事项???

    解决问题

    以下来自《Effective Java》中的读书笔记:

    检查参数有效性

    应该在发生错误之后尽快检测出错误

    例如:

    public BigInteger mod(BigInteger m){
        //尽快检查错误
        if(m.signum() <= 0){
            throw new ArithmeticException("Modulus <= 0:" + m);
        }
        ....
    }
    

    非共有的方法通常应该使用断言来检查它们的参数,具体做法如下:

    private static void sort(long a[],int offset,int length){
        assert a != null;
        assert offset >= 0 && offset <= a.length;
        assert  length >=0 && length <=a.length -offset;
        ....//dosomethiing
    }
    
    必要时进行保护性拷贝

    对于构造器的每个可变参数进行保护性拷贝是必要的

    例如:

    public final class Period{
        private final Date start;
        private final Date end;
        
        public Period(Date start,Date end){
            ....
            this.start = start;
            this.end = end;
        }
        
        public Date start(){
            return start;
        }
        
        public Date end(){
            return end;
        }
    }
    
    Date start = new Date();
    Date end = new Date();
    Period p = new Period(start,end);
    //注意问题出现在这里
    end.setYear(78);
    

    修改后:

    public Period(Date start,Date end){
            ....
         this.start = new Date(start.getTime());
         this.end = new Date(end.getTime());
    }
    

    注意,保护性拷贝是在检查参数有效性之前进行的,并且有效性检查是针对拷贝之后的对象,而不是针对原始的对象。

    虽然替换构造器就可以成功避免上述攻击,但是改变Period实例仍然是有可能的,例如下面例子:

    Date start = new Date();
    Date end = new Date();
    Period p = new Period(start,end);
    //注意问题出现在这里
    p.end().setYear(78);
    

    进一步修改:

    public Date start(){
        return new Date(start.getTime());
    }
        
    public Date end(){
        return new Date(end.getTime());
    }
    

    简而言之,如果类具有从客户端得到或者返回到客户端的可变组件,类就必须保护性的拷贝这些组件。如果拷贝的成本受到限制,并且内信任它的客户端不会不恰当的修改组件,就可以在文档中指明客户端的职责是不得修改受到影响的组件,以此来代替保护性拷贝。

    谨慎设计方法签名
    • 谨慎地选择方法的名称。
    • 不要过于追求提供便利的方法。
    • 避免过长的参数列表。
      有三种方法可以缩短过长的参数列表,第一种是把方法分解成多个方法,每个方法只需要这些参数的一个子集。第二种方法是创建辅助类用来保存参数的分组。第三种是如果方法带有多个参数,尤其是当它们中有些是可选的时候,最好定一个对象来表示所有参数。(这些在之前文章都有提过了)
    慎用可变参数

    在重视性能的情况下,使用可变参数需要特别小心,可变参数方法的每次调用都会导致进行一次数组分配和初始化。如果凭经验确定无法承受这一成本,但又需要可变参数的灵活性,还有一种模式可以让你如愿以偿。假设确定对某个方法95%的调用会有3个或者更少的参数,就声明改方法的5个重载,每个重载方法带有0至3个普通参数,当参数的数目超过3个时,就使用一个可变参数方法:

    例如:

    public void foo(){}
    
    public void foo(int a1){}
    
    public void foo(int a1,int a2){}
    
    public void foo(int a1,int a2,int a3){}
    
    public void foo(int a1,int a2,int a3,int ... rest){}
    

    这种方法可能不太恰当,但是一旦需要它时,它可就帮上大忙了。

    总之,在定义参数数目不定的方法时,可变参数方法是一种很方便的方式,但是它们不应该被过度滥用。如果使用不当,会产生混乱的结果。

    返回零长度的数组或集合,而不是null

    例如:

    public Cheese[] getCheeses(int size){
    
        if(size == 0){
            //这里返回null,是错误的,因为调用改方法,还需要判断这个null对象
            return null;
        }
    }
    

    正确做法:

    private final List<Cheese> cheesesInStock = ...;
    //定义final型的空数组
    private static final Cheese[] EMPTY_CHEESE_ARRAY = new Cheese[0];
    
    public Cheese[] getCheeses(){
        //返回空数组而不是null
        return cheesesInStock.toArray(EMPTY_CHEESE_ARRAY);
    }
    

    同样的对于集合(Collections.emptySet,emptyList,emptyMap等等)

    public List<Cheese> getCheesesList(){
        if(cheesesInStock.isEmpty()){
            //返回不可变的空集合,对于map
            return Collections.emptyList();
        }else{
            return new ArrayList<Cheese>(cheesesInStock);
        }
        
    }
    

    有时候会有人认为:null返回值比零长度数组更好,因为他避免了分配数组所需要的开销。

    为所有导出的API元素编写文档注释

    例如:

    /**
     * <p> the method is ...<i>not<i>
     * Returns the element at the specified position in this list.
     *
     * @param  index index of the element to return
     * @return the element at the specified position in this list
     * @throws IndexOutOfBoundsException {@inheritDoc} if the index is out of..
     */
    public E get(int index) {
        rangeCheck(index);
    
        return elementData(index);
    }
    

    方法的文档注释中每个参数都有一个@param标签(后面应该是一个名词),以及一个@return标签(除非返回为空),以及@throws标签(后面应该是一个名词),@throws标签后应该包含单词"if",描述了异常将在什么样的条件下会被抛出。

    文档注释还使用了HTML标签(<p>和<i>).Javadoc工具会把文档注释翻译成HTML

    **{@code} **:表示该代码片段以代码字体进行呈现

    {@literal}:可以把小于号,大于号等包起来,用来输出这些字符。

    更多的文档注释,可以看《Effective Java》对它的描述。

    读书感悟

    来自亦舒《不易居》

    • 现在还有谁会照顾谁一辈子,那是多沉重的一个包袱。所以非自立不可。
    • 你要改是因为你自己愿意改,不要为任何人,怕只怕那人会令你失望,你又得打回原形。

    其他

    如果有带给你一丝丝小快乐,就让快乐继续传递下去,欢迎转载,点赞,顶,欢迎留下宝贵的意见,多谢支持!

    相关文章

      网友评论

        本文标题:优雅编程之这样编写方法,你就“正常”了(二十八)

        本文链接:https://www.haomeiwen.com/subject/tnbeyttx.html