天天写代码,月月写代码,最后代码写成什么样了?有木有提高,这才是一个程序员成长的关键。今天记录一下最近的一些感悟。
一、保持你的创造力和创新能力
1.劳逸结合
程序猿,咱伤不起啊,被催着加班是常有的事。为了防止挣得多,花得少,死得早发生!咱还是劳逸结合。
今天我就讲讲自己的感悟。曾经一段时间,我连续加班了2~3个月,天天到晚上凌晨回家睡觉,刚毕业嘛,还可以,连续下来,很快的把项目完成了,并且上线了,只要15天,包括产品花的时间,测试时间和接口开发时间。很多工作都并行进行,很多天通宵。。。几个月后发现自己的效率开始大幅下降,智商变低!!一个简单的单词都会卡壳。发现自己在写代码的时候往往采用的是最直接解决问题的办法,没有变通和太多的思考。代码质量可想而知。。。
好吧,看样子上边的状态我是不能在酱紫下去了,我还是好好注意休息吧,劳逸结合,我还想工作到领退休金的那一天(说不定要我80大寿的那天才能退休- -),那个老泪纵横啊。。。
后来,我开始了代码的质量和代码的含金量。我想告诉大家的是,偶尔加班赶上线并不一定真的能搞好一个项目,还是用自己的智慧写代码,会事半功倍,就拿上边这个简单的缓存数据库来说吧。当初要的是实现6个界面数据缓存,每个界面有几十个字段,刚开始是像创建数据库,把解析后的列表存到数据库,这样就要维护6张表,每张表有几十个字段,那不把人搞傻了。。反正耗费时间很长就是了,用了上边这个方法,灵活又简单,还不用关心字段变动。这就是idea!
2.学会分享与交流
你的方法并不一定是最好的方法,要学会分享和交流,才会发现自己的不足,不断的思考才会有较好的方法。团队合作,交流必不可少,程序猿不能只做一个一言不发的编码工具,要多交流思想,随时有个清晰的交流思路,这样以后的成长是非常重要的。分享会让你更深入的思考一样东西,交流可以提高你的表达能力,到了该涨工资的时候千万别憋着,O(∩_∩)O哈哈~。。。
二、程序猿,你要有梦想
我的梦想是。。。。撸出一个吊炸天的游戏。。。。O(∩_∩)O哈哈~,千里之行,始于足下,还是先撸出个帅气的Hello World!!尼玛,太扯了。。。
1.关于未来
我想做什么呢?是给别人老老实实打工,到退休么?不,程序猿这条路不可能干一辈子!!10年对你来说都太长了。。挣得多,花得少,死得早~~那你就要知道自己想要的是什么,未来你的方向是什么。世界没有那么光明,撸代码买房压力山大,那还是想想怎么为自己干活,自己在互联网行业立足吧。你可以大声告诉你的Boss,20年后互联网属于我们,你个糟老头子别嚣张!!O(∩_∩)O哈哈~我以后不会让我的程序猿加班的,会给他们更多的假期的奖金 - -
2.关于现在
天天撸代码,不一样的功能,同样的代码。对于刚毕业一年左右的我们,我觉得该对学习,不断的扩展自己的视野,让自己去学更多的东西。我毕业到现在快半年了,09级的。。我在做Android开发的同时,学了IOS开发,现在又在学Cocos2d-x游戏开发,未来想在手游里干出点什么。。。做应用找不到前途啊。。。。难道是想着20年后还给别人打工??不能够啊!!平时不怎么写博客,今天项目提测了,得写点什么吧。。
3.关于脚步
我想人的价值在于学习和工作,当然只是事业方面的。还有家庭也是很重要的,经营好一份感情,让代码撸得不那么凄凉。。脚踏实地的走,不能太浮躁了,刚毕业就想做出什么吊炸天的东西或者快速学会什么都不现实的,脚踏实地的,一步一步的来,不断积累才是重点。我有很多同学都在急于求成,刚毕业半年就想学会很多,不断的换工作尝试。这是不好的,未来路那么长,我们得认认真真,脚踏实地的走下去,等我们准备好了,再放开手去做,换工作并不能够很好的解决当前的矛盾,关键是我们要知道自己想要的是什么,不能跳来跳去,最后虚度了时光。
三、撸代码的一些建议
撸得一手好代码,把更多的时间花在享受生活上!
1.模块划分
实现一个功能,这个功能可能比较简单,可独立可不独立。功能相似度达到多少以下的时候我们可以单独创建一个界面和类配合实现呢?这个问题在写程序的时候我有点纠结,但是一个同事,他喜欢把很多不同的东西整合在一个里边。这样有的功能区分度不高,导致我添加百度统计的时候的不知道去那统计。首先得赞一下他的整合能力。那么多的东西整合在一起,复杂度显然会比较高,容易导致伤筋动骨的事,改bug就很难了。
关于过多的面相对象知识,这里没办法一一讲解,但是我们得注意,数据建模很重要,把重要的、共用的信息提取出来进行封装会节约我们很多的时间。在封转控件的时候尽量做到与义务逻辑脱离,把需要实现逻辑的东西放到回调接口中来处理。封装的控件尽量只实现接受界面数据的初始化和展示,把数据的操作放到回调接口中来实现。
2.尽量防止重复代码过多
今天干了件很爽的事,一个动画效果需要在6个界面中实现。经过思考和查找发现了8个共有的属性,并且可以提取出来的。。喜出望外啊,我能单独的抽取出这些属性,进行封装岂不是很好。在经过实践之后,我把功能提取出来了。给大家看下:
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewTreeObserver;
import android.view.View.OnTouchListener;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.Animation;
import android.view.animation.Animation.AnimationListener;
import android.view.animation.TranslateAnimation;
import android.widget.AbsListView;
import android.widget.LinearLayout;
import com.demo.widget.search.MySearchToolBar;
/**
* 将搜索栏动画方法封装,抽取出公共部分
* 里边包含了下拉刷新控件,和搜索栏控件
*
* @author hquspring@gmail.com
*
*/
public class SearchToolBarAnimation {
public SearchToolBarAnimation(){
}
/**
* 搜索栏动画
*
* @param searchToolbarHeight
* 搜索栏高度
* @param mSearchToolBar
* 搜索栏控件
* @param mDataListView
* 列表页的自定义ListView
* @param mGapView
* 跟随滚动视图
*/
private void translateTitle(final AnimationItem animationItem) {
if (animationItem.searchToolbarHeight < 1) {
int w = View.MeasureSpec.makeMeasureSpec(0,
View.MeasureSpec.UNSPECIFIED);
int h = View.MeasureSpec.makeMeasureSpec(0,
View.MeasureSpec.UNSPECIFIED);
animationItem.mSearchToolBar.measure(w, h);
animationItem.searchToolbarHeight = animationItem.mSearchToolBar
.getMeasuredHeight();
}
final int searchToolbarHeightTemp = animationItem.searchToolbarHeight;
Animation animation = animationItem.mSearchToolBar.getAnimation();
if (animation != null) {
return;
}
final int visibility = animationItem.mSearchToolBar.getVisibility();
TranslateAnimation translate1 = null;
TranslateAnimation translate2 = null;
if (visibility == View.VISIBLE) {
// mSearchToolBar.setVisibility(View.GONE);
LinearLayout.LayoutParams gapViewParams = new LinearLayout.LayoutParams(
1, 0);
animationItem.mGapView.setLayoutParams(gapViewParams);
translate1 = new TranslateAnimation(0.0F, 0.0F, 0.0F,
-animationItem.searchToolbarHeight);
translate2 = new TranslateAnimation(0.0F, 0.0F,
animationItem.searchToolbarHeight, 0.0F);
// mSearchToolBar.setVisibility(View.GONE);
} else {
translate1 = new TranslateAnimation(0.0F, 0.0F,
0.0F - animationItem.searchToolbarHeight, 0.0F);
translate2 = new TranslateAnimation(0.0F, 0.0F, 0.0F,
animationItem.searchToolbarHeight);
// mSearchToolBar.setVisibility(View.VISIBLE);
}
translate1.setDuration(300);
translate2.setDuration(300);
translate1.setInterpolator(new AccelerateDecelerateInterpolator());
translate2.setInterpolator(new AccelerateDecelerateInterpolator());
translate1.setAnimationListener(new AnimationListener() {
@Override
public void onAnimationStart(Animation arg0) {
}
@Override
public void onAnimationRepeat(Animation arg0) {
}
@Override
public void onAnimationEnd(Animation arg0) {
if (visibility == View.VISIBLE) {
LinearLayout.LayoutParams gapViewParams = new LinearLayout.LayoutParams(
1, 0);
animationItem.mGapView.setLayoutParams(gapViewParams);
animationItem.mSearchToolBar.setVisibility(View.GONE);
} else {
LinearLayout.LayoutParams gapViewParams = new LinearLayout.LayoutParams(
1, searchToolbarHeightTemp);
animationItem.mGapView.setLayoutParams(gapViewParams);
animationItem.mSearchToolBar.setVisibility(View.VISIBLE);
}
animationItem.mSearchToolBar.clearAnimation();
}
});
translate2.setAnimationListener(new AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationRepeat(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
animationItem.mDataListView.clearAnimation();
}
});
animationItem.mSearchToolBar.clearAnimation();
animationItem.mDataListView.clearAnimation();
translate1.setFillEnabled(true);
translate2.setFillEnabled(true);
translate1.setFillAfter(true);
translate2.setFillAfter(true);
animationItem.mSearchToolBar.startAnimation(translate1);
animationItem.mDataListView.startAnimation(translate2);
}
/**
* 获得SearchToolbar的高度
*/
private void getSearchToolbarHeight(AnimationItem animationItem) {
int w = View.MeasureSpec.makeMeasureSpec(0,
View.MeasureSpec.UNSPECIFIED);
int h = View.MeasureSpec.makeMeasureSpec(0,
View.MeasureSpec.UNSPECIFIED);
animationItem.mSearchToolBar.measure(w, h);
animationItem.searchToolbarHeight = animationItem.mSearchToolBar.getMeasuredHeight();
}
/**
* 绑定所有监听器和动画效果
* @param animationItem
*/
public void initAnimation(final AnimationItem animationItem) {
//初始化数据
getSearchToolbarHeight(animationItem);
getTransHeight(animationItem);
if (animationItem.searchToolbarHeight != 0) {
LinearLayout.LayoutParams gapViewParams = new LinearLayout.LayoutParams(
1, animationItem.searchToolbarHeight);
animationItem.mGapView.setLayoutParams(gapViewParams);
}
animationItem.mDataListView
.setOnScrollListener(new ScrollOverListView.OnMyScorllListener() {
@Override
public void onScrollStateChanged(AbsListView view,
int scrollState) {
}
@Override
public void onScroll(AbsListView view,
int firstVisibleItem, int visibleItemCount,
int totalItemCount) {
final int currentFirstVisibleItem = view
.getFirstVisiblePosition();
Log.d("liwt", "onScroll currentFirstVisibleItem:"
+ currentFirstVisibleItem
+ " mLatestFirstVisibleItem:"
+ animationItem.mLatestFirstVisibleItem);
Log.i("liwt", "onScroll mTouchDown:"
+ animationItem.mTouchDown + " mSwitched:"
+ animationItem.mSwitched);
if (!animationItem.mTouchDown
|| !animationItem.mSwitched) {
int type = 0;
if (currentFirstVisibleItem > animationItem.mLatestFirstVisibleItem) {
type = ISlideTabHostListener.SLIDE_DOWN;
if (animationItem.mSearchToolBar
.getVisibility() == View.VISIBLE) {
translateTitle(animationItem);
}
} else if (currentFirstVisibleItem < animationItem.mLatestFirstVisibleItem) {
type = ISlideTabHostListener.SLIDE_UP;
if (animationItem.mSearchToolBar
.getVisibility() == View.GONE) {
translateTitle(animationItem);
}
}
if (type > 0 && animationItem.mTouchDown) {
animationItem.mSwitched = true;
}
}
animationItem.mLatestFirstVisibleItem = currentFirstVisibleItem;
}
});
animationItem.mDataListView.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
Log.i("liwt", "touch action" + event.getAction());
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
animationItem.mTouchDown = true;
break;
case MotionEvent.ACTION_OUTSIDE:
case MotionEvent.ACTION_UP:
animationItem.mTouchDown = false;
animationItem.mSwitched = false;
break;
}
return false;
}
});
}
/**
* 获得执行动画的高度
*/
private void getTransHeight(final AnimationItem animationItem) {
// 注意:如下获得控件高度的方法对设置了Visiblity=gone 属性的控件无效。
ViewTreeObserver vto2 = animationItem.mDataListView.getViewTreeObserver();
vto2.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
@SuppressWarnings("deprecation")
@Override
public void onGlobalLayout() {
animationItem.mDataListView.getViewTreeObserver()
.removeGlobalOnLayoutListener(this);
animationItem.translateHeight = animationItem.mDataListView.getHeight();
if (animationItem.translateHeight != 0) {
animationItem.mSearchToolBar.setTranslateHeight(animationItem.translateHeight);
}
}
});
}
public static class AnimationItem {
public static AnimationItem getInstance() {
return new AnimationItem();
}
public boolean mTouchDown = false;
public boolean mSwitched = false;
public int mLatestFirstVisibleItem = 0;
public View mGapView = null;
/** 搜索工具条的高度 */
public int searchToolbarHeight = 0;
/** 搜索结果列表 **/
public PullDownView mDataListView = null;
/** 搜索条件工具条 */
public MySearchToolBar mSearchToolBar = null;
/** 动画执行高度*/
public int translateHeight;
}
public interface ISlideTabHostListener {
public static final int SLIDE_UP = 1;
public static final int SLIDE_DOWN = 2;
public void slide(int slideType);
}
}
注:以上是一个PullDownListView ,加一个自定义搜索工具条的代码,它实现的是搜索工具条自动收放。避免了6*300行的重复代码。原来程序员也可以开心一刻。。。
3.封转方法的时候对方法的调用约束尽量降低、程序的健壮性要强
再给一个经典的do{}while(false)的方法,做到写程序优雅,严谨,容易查找bug。
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.text.TextUtils;
import com.<span style="font-family: Arial, Helvetica, sans-serif;">demo</span><span style="font-family: Arial, Helvetica, sans-serif;">.database.TableCacheDatabase;</span>
import com.<span style="font-family: Arial, Helvetica, sans-serif;">demo</span>.globleData.GlobleData;
/**
* 来电列表数据缓存数据库
*
* @author hquspring@gmail.com
*
*/
public class ListDataCache {
/** 缓存使用者的id*/
private static final String USERID = "USERID";
/** 我的卖车缓存 */
private static final String MYSELLCARCACHE = "MYSELLCARCACHE";
/** 我的买车缓存*/
private static final String MYBUYCARCACHE = "MYBUYCARCACHE";
/** 搜索卖车缓存 */
private static final String STORESELLCARCHE = "STORESELLCARCHE";
/** 搜索买车缓存 */
private static final String STOREBUYCARCHE = "STOREBUYCARCHE";
/** 我的来电列表缓存 */
private static final String MYPHONECARCHE = "MYPHONECACHE";
/** 店内来电列表缓存 */
private static final String STOREPHONECARCHE = "STOREPHONECACHE";
private static final String TABLENAME = "CACHETABLE";
public static final String CREATETABLESQL = "CREATE TABLE IF NOT EXISTS "+
TABLENAME+ " ( "+
MYSELLCARCACHE + " text, "+
MYBUYCARCACHE + " text, " +
STORESELLCARCHE +" text, "+
STOREBUYCARCHE + " text, " +
MYPHONECARCHE + " text, "+
STOREPHONECARCHE + " text, "+
USERID + " text NOT NULL);";
public enum CACHETYPE{
TYPE_MYSELLCARCACHE,
TYPE_MYBUYCARCACHE,
TYPE_STORESELLCACHE,
TYPE_STOREBUYCACHE,
TYPE_MYPHONECACHE,
TYPE_STOREPHONECACHE
};
private SQLiteDatabase db;
private TableCacheDatabase cache;
private Context context;
public ListDataCache(Context context){
this.context = context;
cache = new TableCacheDatabase(context);
}
/**
* 将json数据放入缓存
* @param value json值
* @param TYPE 缓存位置枚举位置
*/
public void addDataToCache(String value,CACHETYPE TYPE){
do{
if(TextUtils.isEmpty(value)){
continue;
}
if(cache==null){
continue;
}
db = cache.getWritableDatabase();
if(db==null){
continue;
}
Cursor cursor = db.query(TABLENAME, null, USERID+"=?", new String[]{GlobleData.getUserInfo(context).userID}, null, null, null);
if(cursor==null){
continue;
}
ContentValues values = getContentValues(value,TYPE);
if(values==null||values.size()==0){
continue;
}
//这里是查找性的插入或更新,其实我觉得这里可以由SQL语句优化,不用那么麻烦的去,一条SQL查找兼插入或更新岂不更好- -
if(cursor.moveToFirst()){
if(!cursor.isAfterLast()){
db.update(TABLENAME, values, USERID+"=?",new String[]{GlobleData.getUserInfo(context).userID});
}
}else{
values.put(USERID, GlobleData.getUserInfo(context).userID);
db.insert(TABLENAME, null,values);
}
cursor.close();
db.close();
values.clear();
}while(false);
}
/**
* 获取ContentValues 根据值和类型
* @param value
* @param TYPE
* @return ContentValues
*/
private ContentValues getContentValues(String value,CACHETYPE type){
ContentValues values = new ContentValues();
switch(type){
case TYPE_MYSELLCARCACHE:{
values.put(MYSELLCARCACHE, value);
}
break;
case TYPE_MYBUYCARCACHE:{
values.put(MYBUYCARCACHE, value);
}
break;
case TYPE_STORESELLCACHE:{
values.put(STORESELLCARCHE, value);
}
break;
case TYPE_STOREBUYCACHE:{
values.put(STOREBUYCARCHE, value);
}
break;
case TYPE_MYPHONECACHE:{
values.put(MYPHONECARCHE, value);
}
break;
case TYPE_STOREPHONECACHE:{
values.put(STOREPHONECARCHE, value);
}
break;
default:break;
}
return values;
}
/**
* 获取当前列的位置
* @param cursor
* @param TYPE
* @return index
*/
private int getIndexByType(Cursor cursor, CACHETYPE type){
int index = 0;
switch(type){
case TYPE_MYSELLCARCACHE:{
index = cursor.getColumnIndex(MYSELLCARCACHE);
}
break;
case TYPE_MYBUYCARCACHE:{
index = cursor.getColumnIndex(MYBUYCARCACHE);
}
break;
case TYPE_STORESELLCACHE:{
index = cursor.getColumnIndex(STORESELLCARCHE);
}
break;
case TYPE_STOREBUYCACHE:{
index = cursor.getColumnIndex(STOREBUYCARCHE);
}
break;
case TYPE_MYPHONECACHE:{
index = cursor.getColumnIndex(MYPHONECARCHE);
}
break;
case TYPE_STOREPHONECACHE:{
index = cursor.getColumnIndex(STOREPHONECARCHE);
}
break;
default:break;
}
return index;
}
/**
* 获取缓存数据根据位置
* @param TYPE 位置枚举类型
* @return 缓存的json数据
*/
public String getDataCache(CACHETYPE type){
String value = "";
do{
if(cache==null){
continue;
}
db = cache.getWritableDatabase();
if(db==null){
continue;
}
Cursor cursor = db.query(TABLENAME, null, USERID+"=?", new String[]{GlobleData.getUserInfo(context).userID}, null, null, null);
if (cursor == null){
continue;
}
if(cursor.moveToFirst()){
if(!cursor.isAfterLast()){
int index = getIndexByType(cursor, type);
value = cursor.getString(index);
}
}
cursor.close();
db.close();
}while(false);
return value;
}
}
注:以上是缓存数据用的一个简易数据库缓存,它需要缓存6中不同的数据,涉及到缓存的添加和更新,怎么去管理这个缓存呢??当然枚举类型是一种合适的方法,还要加上一些严谨的代码风格,习惯了。写起代码会比较好管控bug,写完的代码心里也有底。
小结一下:面相对象的编程思想,还有代码的优化和代码规范是每个程序员成长中的重点吧,写了段时间的程序,今天有空了,就总结一下。