写给所有程序员_回顾学习初期的五个为什么

变量,函数(方法),类与对象,接口,抽象类

实际上,开始学习的java的时候,这些是基础,也是疑惑。老师填鸭似的把这些知识塞给我,然后完全不知道为什么要用,甚至刚刚学习完java还没有做项目的时候,我对我的老师说,我虽然会用这些东西,但是完全不知道什么时候要用,我为什么要用他们。

现在,似乎有点明白了为什么要用这些东西,于是我决定写下来。

如果你是一位面向过程方面的程序员请看前两个就够了,第三个可近似理解成结构体,面向对象的程序员请看全部。如果你已经学习了一段时间,我认为你仍然有必要看看,因为我讲的东西稍微有点特别,你应该没有完全听过。

1.为什么要用变量

(1)并非所有的数据都是已知的。

比如你获取电脑的宽度,比如你获取一些网络数据,这些东西获取之前,你不知道它是什么,只知道类型或者大概的格式。偏偏你的程序中还要对他们进行处理,于是你需要使用变量,用它来保存你获取的未知事物,方便进行逻辑处理

(2)变量有助于记忆。

变量类型帮助我们记忆变量大概的范围,相当于量词:一台电脑的台,一只喜鹊的只。从台字我们大概了解到这件东西有个底座,是个静物;从只字大概了解到应该是动物类的。
变量名帮助我们了解变量的用途,具体指的是什么,就像生活中的所有事物,我们都会用个名字来代替他们方便交流,如电脑,手机,书包。。。

所以,变量类型要准确,变量名要清楚,这是对一个变量的基本要求。

如何做到变量类型要准确?

对于基本类型,在能不使用引用类型的情况下就不使用。对于一个整型数字比如5,我可以用int类型表示:
int num = 5;也可以用String类型表示String num = “5”;但相比较而言,整型int保存数字更加合理,所以我们用整型。当然,像计算金额这种情况,使用BigDecimal当然会比double,float更加合理,所以我后面加了一句话在能不使用引用类型的情况下就不使用
对于引用类型,类型名称要尽量准确。假如你需要在网络请求后返回图书详细信息,可以写成BookInfo。但是通常我们在图书列表中会返回图书简略信息,所以这个名称的识别率就变差,这时我们可以加个形容词,变成:DetailBookInfo,外面的列表的简略图书信息可用SimpleBookInfo。

注意,我这里写DetailBookInfo而非BookDetailInfo,因为看起来DetailBookInfo和SimpleBookInfo更容易区分,BookDetailInfo和BookSimpleInfo没那么容易区分。

如何做到变量名清楚?

首先,变量名的清楚是相对而言的
简单举个例子,某小学有个同学叫张三丰,如果只有一个张三丰,老师叫他的时候只需要说“张三丰”,这位同学就知道叫的是他了。然后发现原来权限有好几个张三丰,而我们班只有一个张三丰,那么老师如果叫的是我们班的张三丰,老师会说“X年X班张三丰”。然后某天,我们班又来了一个张三丰,这家伙比较胖。所以老师叫他的时候,会说“X年X班胖三丰”,另一个叫“X年X班瘦三丰”,不同的变量名称根据识别尺度的改变而发生改变。

第二,名字要一看就明白,不要不明觉厉。不要起a,b这种完全分不清是啥的变量名。前面举得例子,老师叫张三丰,然后说“A,你出来一下。”张三丰知道叫的是他吗?

如果我只要求1-100的和,这个和使用sum作为变量名就可以了。但如果我既需要求1-100的和也需要求1-1000的和,那么单单用的sum1,sum2就不够清楚了,因为单单从名称看不出来谁是1-100的和。那么可以这样取名:
sumOfOne2Hundred,sumOfOne2Thousand,注意这里我没有用one2HundredSum和one2ThousandSum,因为那样不容易识别。

2.为什么要用函数(方法)

(1)分类查找

比如我们要把大象塞进冰箱分成三步:第一步,打开冰箱,第二步,把大象放进冰箱,第三步,把冰箱门关上。我把他写成这样:

public void fillElephantInRefrigerator(){
    openRefrigerator();
    putElephantInRefrigerator();
    closeRefrigerator();
}

public void openRefrigerator(){
    //此处略去100行
}

public void putElephantInRefrigerator(){
    //此处略去100行
}

public void closeRefrigerator(){
    //此处略去100行
}

假设每个步骤都需要写100行代码,如果我比较关心putElephantInRefrigerator()这一步我做了什么,因为我写了函数,所以只需要看:openRefrigerator(),不,我不关心这个,然后看到putElephantInRefrigerator(),2行代码,就找到了正确的位置。如果我没有用函数,看起来是这样的:

public void fillElephantInRefrigerator(){
    //此处略去100行
    //此处略去100行
    //此处略去100行
}

这样我需要看到101行才能找到putElephantInRefrigerator()这部分我做了什么,而且还不一定找的到。为什么说不一定找的到?请看第二点。

(2)给步骤具体的含义

上面的例子中,如果不写成具体的函数(方法),一堆代码很容易看的我们晕头转向,我们不会知道第一个100行是用于打开冰箱的,因为我代码中并没有提到这一点。但是,函数名能够告诉我们。所以和变量名相同,不要起没意义和没区分度的名字。

(3)方便重复调用

比如我要把两只大象塞入冰箱,我只要加上这段代码:

fillElephantInRefrigerator();
fillElephantInRefrigerator();

如果我要把100只大象塞入冰箱,我只要这样写:

int elephantNum = 1;
for(int elephantNum = 1; elephantNum <= 100; elephantNum++){
    fillElephantInRefrigerator();
}

有的时候,在不同的地方把大象塞入冰箱,那么我们可以在每个需要的地方调用它。当我认为一部分代码可能经常使用的时候,我往往会把它写成一个函数。

(4)特殊的一句话函数

有的时候,我们把一行代码单独写个函数,有一下几种情况:
a.这是一个Bean或者其他对象,我需要写getter,setter方法
public class Apple{
public int color;
public void setColor(){
this.color = color;
}
public int getColor(){
return color;
}
}

b.给逻辑判断一定的含义
对于选择结构的判断语句,有时候判断的内容不止一句,通过&&和||连接,有的时候他们太过复杂,一眼看不出在判断什么,这里通过isGood()函数判断了是否是良好。

int score = 82;
if(isGood(score)){
    System.out.println("良好");
}

public boolean isGood(score){
    return score >= 75 && score < 90;
}

c.返回特定值,使用三目运算符

public int getVisiblity(View v){
    return v.getVisibility()==View.VISIBLE?1:0;
}

3.为什么使用类与对象

举个简单的小例子:比如你是公司的老板,公司刚刚起步,只有你一个人,于是你什么都要做,因为公司前面的订单并不多,所以你一个人忙的过来。然后,有一天,你一夜暴富,把公司开到和阿里巴巴一样大,这个时候你需要每天处理上亿订单。

然后,你需要大量的招募人员,开始的时候每个人做一部分,这时候,你就需要权责划分。权责划分的过程,就是产生对象的过程。

简单的说:对象产生的过程就是分工的过程。那么,有哪些类呢?

(1)数据类,表示一种类型事物的特征,纯粹用于特征描述,方便数据传输,数据返回(Bean)

public class Apple{
    private double weight;
    private String description;

    //getter and setter
}

数据传输时,我们可以很方便的把它作为参数,传到函数中处理,当有多个返回值时,可以通过数据类打包一起返回。

这种类的特点是,除了属性,基本是getter和setter的方法。当然有的时候会有些特殊的,比如说我需要所有的特征信息,上述类可能加入以下代码:

public void printFeatures(){
    System.out.println("重量是:"+weight+" 描述是"+description);
}

(2)同类事物管理

比如,我们要把Apple保存在缓存中,为了识别,加一个编号。

public class Apple{
    private double weight;
    private String description;
    private int appleID;

    //getter and setter

    @Overriade
    public boolean equals(Object o){
        if(o == null)
            return false;
        if(o instance of Apple)
            return appleID == ((Apple)o).getAppleID();

        return false;
    } 
}
public class AppleCacheManager{
    private List cacheApples = new ArrayList<>();

    public boolean hasApple(Apple apple){
        return cacheApple.contains(apple);
    }   

    public void addApple(Apple apple){
        cacheApples.add(apple);
    }

    public void removeApple(Apple apple){
        if(hasApple(apple))
            cacheApples.remove(apple);
    }

    public void clearCache(){
        cacheApples.clear();
    }
}

(3)工具类

用于处理同一类事物,一般命名为XXUtil,把该类事物可能进行的处理函数归纳到一起,比如打印输出类,这种类一般由静态方法组成,也有对象类的,较少一些。

public class PrinterUtil{
    public static void print(){
        System.out.println("这句话用于测试Printer是否可用");
    }

    public static void print(int num){
        System.out.println("这个数字是:"+num);
    }

    public static void print(double num){
        System.out.println("这个小数是:"+num);
    }

    public static void print(String num){
        System.out.println("这句话是:"+num);
    }
}

(4)界面类

这个不列举了,反正一放上去可以立刻能看到的就是界面。

(5)逻辑处理类,MVP的presenter或者MVC的Controller

这类是把ui的逻辑进行处理,对于没用过设计模式的比较难描述,我还是用文字吧。如果界面有这样一个功能:点击按钮,把一些文字显示到文本框,我把按钮放在界面,把文字放到数据类中,获取文本信息这一步就通过presenter或者controller来处理。可能我要给这些文字加前缀或者后缀,也在presenter或者controller中来处理。

(6)操作类,把对某种类型的操作封装在单独的类中,通常用于对同类对象的统一操作进行集成,添加一定的处理,或者是对于唯一对象的管理,避免出现多个对象(网络,数据库等)

public class ListProduce{
    private List mList = new ArrayList();
    public void add(Object obj){
        mList.add(obj);
        System.out.println("添加了一条数据");
    }

    public void remove(){
        mList.remove(obj);
        System.out.println("删除了一条数据");
    }

    public List copy(){
        List tempList = new ArrayList();
        tempList.addAll(mList);
        return tempList;
    }
}

大概就这么多,其他的有关框架和设计模式的部分就不讲了。前期需要用的基本就这几种。简而言之:类的存在的目的在于传输数据,处理数据,显示界面,如果一段逻辑可以用于传输特定类型的数据,处理特定类型的数据,显示某种特定的界面,那么就可以把这段逻辑独立成类。

为什么要使用抽象类

抽象类常用于处理一堆同类事物共同要做的事。比如网页,每个网页都会有显示加载等待过程,加载网址内容,显示崩溃内容(404等),类似这种同类事物都要进行处理,并且可能需要相同处理过程的我们用抽象类:

public abstract class SimulationBrowser{
    public void create(){
        showLoadingDialog();
        loadData();
    }

   public void showLoadingDialog(){
        System.out.println("显示加载动画");
    }

    public void loadData(){
        //进行网络异步请求。。。这里用个线程假装一下,因为网络的篇幅太长。。。
        new Thread(){
            public void run(){
                try{
                    Thread.sleep(5000);
                    printSuccessLog();
                }catch(Exception e){
                    e.printStackTrace();
                    printErrLog();
                }finally{
                    dismissDialog();
                }
            }
        }.start();
    }

    public abstract void printSuccessLog();
    public abstract void printErrLog();
    public void dismissDialog(){
        System.out.println("隐藏加载动画");
    }
}

上面的抽象类在子类调用create方法后正式工作,显示显示加载动画,然后在加载之后进行网络请求(模拟),然后由子类重写printSuccessLog(),和printErrLog()进行处理显示错误信息。可以看到,我们的抽象类将显示加载动画,进行网络请求这些步骤进行统一处理,然后对于部分不确定的工作使用抽象方法交给子类继续完成,这就是抽象类需要做的。有经验的程序员会在可能加抽象类的地方预先加抽象类,用于程序扩展。
简单的说,抽象类就是统一这些类都要做的事,对于不确定的事交给继承他的的类做。

为什么要用接口

这是我学习初期最疑惑的问题,因为很多东西不用它也可以解决,为什么要用接口呢?

(1)接口用在我们纯粹知道要做哪些事情,但不知道具体内容的时候(较少用)。

假如每个需要工作的人都需要上班下班的考勤,假如你是清洁工早上5点以后到岗算迟到,假如你是工人,每天6点以后到岗是迟到,假如你是办公室职员,每天9点以后到岗是迟到,因为考勤时都需要判断是否迟到,所以制定统一接口:

public interface Attendance{
    boolean isLate();
}
public class Cleaner implements Attendance{
    public void printWork(){
        System.out.println("打扫卫生");
    }

    public boolean isLate(){
        Calendar mCalendar = Calendar.getInstance();
        int hour = mCalendar.get(Calendar.HOUR_OF_DAY); 
        return hour > 5;
    }
}
public class Worker implements Attendance{
    public void printWork(){
        System.out.println("搬砖");
    }

    public boolean isLate(){
        Calendar mCalendar = Calendar.getInstance();
        int hour = mCalendar.get(Calendar.HOUR_OF_DAY); 
        return hour > 6;
    }
}
public class Officer implements Attendance{
    public void printWork(){
        System.out.println("打字");
    }

    public boolean isLate(){
        Calendar mCalendar = Calendar.getInstance();
        int hour = mCalendar.get(Calendar.HOUR_OF_DAY); 
        return hour > 9;
    }
}

(2)使用接口来做监听器或回调(很频繁)

大多时候接口是用这种,这种方式的好处是:灵活,可以返回多个数据。下列例子是个最简单的回调。
这里我期望通过getDateTime获取两个返回值日期和时间,正常情况下只能返回一个,所以使用回调。

public class CallbackTester(){
    public static void main(String[] args){
        CallbackProducer producer = new CallbackProducer();
        producer.getDateTime(new DateTimeListener{
            public void append(String date,String time){
                System.out.println("日期是:"+date);
                System.out.println("时间是:"+time);
            }
        });
    }
}
public interface DateTimeListener{
    void append(String date,String time);
}
public CallbackProducer{
    public void appendAfterDate(DateTimeListener listener){
        Calendar mCalendar = Calendar.getInstance();
        Date nowDate = new Date(); 
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd");
        SimpleDateFormat timeFormat = new SimpleDateFormat("HH:mm:ss");
        String date= dateFormat.format(nowDate); 
        String time= dateFormat.format(nowDate); 

        if(listener!=null)
            listener.append(date,time);
    }
}

另外一点,回调能实现先处理后调用
把上面的方式稍微改变一下:

public class CallbackTester(){
    public static void main(String[] args){
        CallbackProducer producer = new CallbackProducer();
        producer.setOnDateTimeListener(new DateTimeListener{
            public void append(String date,String time){
                System.out.println("日期是:"+date);
                System.out.println("时间是:"+time);
            }
        });
        producer.getDateTime();
    }
}
public interface DateTimeListener{
    void append(String date,String time);
}
public CallbackProducer{
    private DateTimeListener listener;
    public void setOnDateTimeListener(DateTimeListener listener){
        this.listener = listener;
    }

    public void appendAfterDate(){
        Calendar mCalendar = Calendar.getInstance();
        Date nowDate = new Date(); 
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd");
        SimpleDateFormat timeFormat = new SimpleDateFormat("HH:mm:ss");
        String date= dateFormat.format(nowDate); 
        String time= dateFormat.format(nowDate); 

        if(listener!=null)
            listener.append(date,time);
    }
}

这里我们把调用listener和初始化分开,在初始化过程中:

producer.setOnDateTimeListener(new DateTimeListener{
            public void append(String date,String time){
                System.out.println("日期是:"+date);
                System.out.println("时间是:"+time);
            }
        });

这一部分可以在调用前先处理返回了这些值要做什么。然后到这一步:

producer.getDateTime();

时才会调用接口,否则永远都不会调用。这种把处理和调用分开的现象我们称之为解耦

总之,接口用于规定一类事物共同要做的事,可以间接实现了多返回值,能够解耦。

总结:

1.为什么使用变量?
变量能够用于处理数据,好的变量名能够便于记忆。

2.为什么使用函数?
函数能够将程序进行分区管理,方便查找和修改,赋予步骤具体的含义。

3.为什么使用类与对象?
我们需要对数据进行传输,处理,显示,类与对象是媒介。

4.为什么使用抽象类?
为了统一处理一类事物共同要做的事情的步骤顺序,对于不能处理的具体事务,交给子类去处理。

5.为什么使用接口?
接口能够规定事务的共同需求(无法写出具体的步骤,零散),实现多返回值,解耦。

之所以写这篇文章是想提醒大家一个非,不问题。
例如:如果一个变量不能方便我们处理数据,变量名不能够便于记忆,那么就证明我们这个变量写的不够好。如果是第一条,或许要改变一下使用方式,让变量更便捷易用;如果是第二条,那么就要考虑一个合适的名字,让变量更易读。

如果一个东西不能满足它的需求,那么它就是残次品,要么删除,要么整改。希望读者不要忘记这些本质的东西。

你可能感兴趣的:(归纳整理)