写给所有程序员_回顾学习初期的五个为什么
2017-10-17 17:33
513 查看
变量,函数(方法),类与对象,接口,抽象类
实际上,开始学习的java的时候,这些是基础,也是疑惑。老师填鸭似的把这些知识塞给我,然后完全不知道为什么要用,甚至刚刚学习完java还没有做项目的时候,我对我的老师说,我虽然会用这些东西,但是完全不知道什么时候要用,我为什么要用他们。
现在,似乎有点明白了为什么要用这些东西,于是我决定写下来。
如果你是一位面向过程方面的程序员请看前两个就够了,第三个可近似理解成结构体,面向对象的程序员请看全部。如果你已经学习了一段时间,我认为你仍然有必要看看,因为我讲的东西稍微有点特别,你应该没有完全听过。
变量名帮助我们了解变量的用途,具体指的是什么,就像生活中的所有事物,我们都会用个名字来代替他们方便交流,如电脑,手机,书包。。。
所以,变量类型要准确,变量名要清楚,这是对一个变量的基本要求。
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,因为那样不容易识别。
假设每个步骤都需要写100行代码,如果我比较关心putElephantInRefrigerator()这一步我做了什么,因为我写了函数,所以只需要看:openRefrigerator(),不,我不关心这个,然后看到putElephantInRefrigerator(),2行代码,就找到了正确的位置。如果我没有用函数,看起来是这样的:
这样我需要看到101行才能找到putElephantInRefrigerator()这部分我做了什么,而且还不一定找的到。为什么说不一定找的到?请看第二点。
如果我要把100只大象塞入冰箱,我只要这样写:
有的时候,在不同的地方把大象塞入冰箱,那么我们可以在每个需要的地方调用它。当我认为一部分代码可能经常使用的时候,我往往会把它写成一个函数。
a.这是一个Bean或者其他对象,我需要写getter,setter方法
public class Apple{
public int color;
public void setColor(){
this.color = color;
}
public int getColor(){
return color;
}
}
[b]b.给逻辑判断一定的含义
对于选择结构的判断语句,有时候判断的内容不止一句,通过&&和||连接,有的时候他们太过复杂,一眼看不出在判断什么,这里通过isGood()函数判断了是否是良好。
c.返回特定值,使用三目运算符
然后,你需要大量的招募人员,开始的时候每个人做一部分,这时候,你就需要权责划分。权责划分的过程,就是产生对象的过程。
简单的说:对象产生的过程就是分工的过程。那么,有哪些类呢?
数据传输时,我们可以很方便的把它作为参数,传到函数中处理,当有多个返回值时,可以通过数据类打包一起返回。
这种类的特点是,除了属性,基本是getter和setter的方法。当然有的时候会有些特殊的,比如说我需要所有的特征信息,上述类可能加入以下代码:
大概就这么多,其他的有关框架和设计模式的部分就不讲了。前期需要用的基本就这几种。简而言之:类的存在的目的在于传输数据,处理数据,显示界面,如果一段逻辑可以用于传输特定类型的数据,处理特定类型的数据,显示某种特定的界面,那么就可以把这段逻辑独立成类。
上面的抽象类在子类调用create方法后正式工作,显示显示加载动画,然后在加载之后进行网络请求(模拟),然后由子类重写printSuccessLog(),和printErrLog()进行处理显示错误信息。可以看到,我们的抽象类将显示加载动画,进行网络请求这些步骤进行统一处理,然后对于部分不确定的工作使用抽象方法交给子类继续完成,这就是抽象类需要做的。有经验的程序员会在可能加抽象类的地方预先加抽象类,用于程序扩展。
简单的说,抽象类就是统一这些类都要做的事,对于不确定的事交给继承他的的类做。
这里我期望通过getDateTime获取两个返回值日期和时间,正常情况下只能返回一个,所以使用回调。
另外一点,回调能实现先处理后调用。
把上面的方式稍微改变一下:
这里我们把调用listener和初始化分开,在初始化过程中:
这一部分可以在调用前先处理返回了这些值要做什么。然后到这一步:
时才会调用接口,否则永远都不会调用。这种把处理和调用分开的现象我们称之为解耦。
总之,接口用于规定一类事物共同要做的事,可以间接实现了多返回值,能够解耦。
变量能够用于处理数据,好的变量名能够便于记忆。
2.为什么使用函数?
函数能够将程序进行分区管理,方便查找和修改,赋予步骤具体的含义。
3.为什么使用类与对象?
我们需要对数据进行传输,处理,显示,类与对象是媒介。
4.为什么使用抽象类?
为了统一处理一类事物共同要做的事情的步骤顺序,对于不能处理的具体事务,交给子类去处理。
5.为什么使用接口?
接口能够规定事务的共同需求(无法写出具体的步骤,零散),实现多返回值,解耦。
之所以写这篇文章是想提醒大家一个非,不问题。
例如:如果一个变量不能方便我们处理数据,变量名不能够便于记忆,那么就证明我们这个变量写的不够好。如果是第一条,或许要改变一下使用方式,让变量更便捷易用;如果是第二条,那么就要考虑一个合适的名字,让变量更易读。
如果一个东西不能满足它的需求,那么它就是残次品,要么删除,要么整改。希望读者不要忘记这些本质的东西。
实际上,开始学习的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]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<Apple> 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 cla d3ff ss 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.为什么使用接口?
接口能够规定事务的共同需求(无法写出具体的步骤,零散),实现多返回值,解耦。
之所以写这篇文章是想提醒大家一个非,不问题。
例如:如果一个变量不能方便我们处理数据,变量名不能够便于记忆,那么就证明我们这个变量写的不够好。如果是第一条,或许要改变一下使用方式,让变量更便捷易用;如果是第二条,那么就要考虑一个合适的名字,让变量更易读。
如果一个东西不能满足它的需求,那么它就是残次品,要么删除,要么整改。希望读者不要忘记这些本质的东西。
相关文章推荐
- 为什么应该投资程序员的学习?
- SQL 数据库 学习 003 什么是数据库? 为什么需要数据库?是不是所有的软件都是用Sql Server?
- 为什么每个程序员都应该学习使用命令行
- 写给师弟师妹的一封信-论在校程序员的学习方向
- 写给自己的Java程序员学习路线图
- 为什么每个程序员都要学C语言的五个理由
- 写给所有程序员_if...else简化研究
- 为什么每个程序员都应学习代码编译器知识
- 我建议所有有追求的程序员一定要把英语当做最基本的一门编程语言来学习
- 一个.NET程序员为什么学习Ruby on Rails?
- 程序员为什么要学习vim?
- 为什么每个程序员都应该学习C语言?
- 为什么每个程序员都应该学习C语言?
- 写给自己的Java程序员学习路线图
- Windows8的到来,让所有程序员必须彻底的刷新自己,否则将被淘汰!学习HTML5
- 为什么大龄程序员都会选择学习大数据?
- 对于程序员, 为什么英语比数学更重要? 如何学习
- 为什么ASP.NET程序员应该学习CSS?
- Reactjs-程序员为什么应该学习Android 开发
- 为什么你永远是菜鸟——写给所有新人