程序员之网络安全系列(一):为什么要关注网络安全?

假如,明明和丽丽相互不认识,明明想给丽丽写一封情书,让隔壁老王送去

  1. 如何保证隔壁老王不能看到情书内容?(保密性)
  2. 如何保证隔壁老王不修改情书的内容?(完整性)
  3. 如何保证隔壁老王不冒充明明?(身份认证)
  4. 如何保证明明不能否认情书是自己写的?(来源的不可否认)

前言

大家都知道最近几年闹的沸沸扬扬的网络安全事件,之前的CSDN密码泄露,不久前的网易邮箱密码泄露,那么如果你的密码泄露,除了本身的网站外,还有很多人其它很多地方甚至银行密码都使用相同的密码,从而带来了很大的麻烦,据说“半个” 互联网的库都被人拖过。

亲身经历

拿我自己经历的一件事来说,前不久,我和一个朋友一起定了趟机票去上海,出发前两小时,收到一个短信说我预定的航班由于天气原因被取消,我需要改签另一个航班,让我拨打航空公司400的电话,由于马上要出发去机场,也没有去查具体航空公司的电话,就打了这个电话,对方的整套系统客服系统和流程几乎和航空公司一模一样,知道我的姓名,身份证,订的航班号等等都一清二楚,最后再说改签需要给用户补偿,需要给你转钱,所以你需要提供银行卡号,然后确认后才能改签,我这样说大家可能觉得如果是自己不太可能上当,但是你们都忽略了人的心里因素,第一你的私密信息他都知道(就像一个陌生人让你爸转钱说你需要钱,说是你的好朋友,知道你的所有只有你爸和你知道的信息), 第二你很着急,因为你在目的地的酒店已定,后面去别的地方的机票、火车票已定,所以你必须这个时间起飞等等。

最后我没有上当的原因,是因为我问骗子,航站楼是哪一个,他说是4号航站楼,因为西安就没有4号航站楼,当然这个应该是骗子的失误。

我们所有的人觉得自己百分之百不会上当的人,那是因为你没碰上骗子高手!

骗子可以得逞的罪魁祸首

我总结了大部分骗子能够骗成功的最主要的原因,是因为我们的私密信息被泄露了!可见网络安全的重要性,而针对我的这个事件,我不知道是航空公司还是某订票网站把我的信息泄露,当然,骗子可能组合多个地方的泄露信息。

而这些信息系统是谁开发和维护的呢? 是程序员!

所以我们程序员需要学习安全知识,保护用户数据,同时防止自己被骗,对那些安全性不高的网站尽量不要使用,对那些安全不高的系统尽量不使用。

作为一个多年的程序员,我对网络安全相关的知识也非常少,我知道一些常规的东西,比如敏感数据加密存储,网站尽量使用https等,但是由于对后面的原理知道的太少,所以有的时候不一定做出了正确地选择,直到最近有的程序员说密码MD5存储了,就一定是安全的,这让我感觉了害怕,而且我看有的系统也真的只是MD5了一下,所以我开始决定学点安全的常识,记录一点“大家” 常用的程序安全知识, 在这里和大家共同进步,由于我对这对理解的不深,要学习的东西很多,所以也希望大家帮忙指出错误的地方。

敏捷实践系列(八):单元测试及最佳实践

前言

在工作中或者在面试中,我经常碰到的开发人员就是对单元测试不重视,这一类基本上都表现出了一种“无知的自信”,总觉得自己写的代码质量很高,直到一次次虫子(Bug)把自己咬的头破血流时,才发现原来自己的代码已经到了剪不断理还乱的状态,而每一次修改一个bug,都需要走一遍“墨镜迷宫” (看上图)。还有很多人知道单元测试或者写出了单元测试,但是就是写了一个方法,上面标注了一个[Test]属性而已,甚至很多的人单元测试上面标注的是[IgnoreTest], 每次看见这些,我都深深的感到推行单元测试之路是艰难的,是遥远的,但是我依然坚信是是渴望也可及的,只要有着深深的信念,坚强的意志,无谓的勇气,一头扎进去泥巴堆里,假以时日,当大雨来临,必将带走泥巴,从此你拔剑扬眉,哦,你不用拔剑了,因为你就是剑。。。

为了让更多人能够拔剑扬眉,也为了我们公司刚入职的新人做一些培训,我精心准备了单元测试的一些知识,在此为你奉上,我尽量用简短的语句来描述,如果你不清楚我说的某一些点,那么欢迎你发邮件给我 wangdeshui@outlook.com,我可以针对集中的点发篇文章,如果你想知道我说的所有点怎么实践,那就联系我,试试加入到我们公司来。

什么是单元测试

单元测试是开发者编写的一小段代码,用于检验被测代码中的一个很明确的功能是否正确。通常而言,一个单元测试是用于判断某个特定条件(或者场景)下某个特定函数的行为。

执行单元测试,是为了证明某段代码的行为确实和开发者所期望的一致。因此,我们所要测试的是规模很小的、非常独立的功能片段。通过对所有单独部分的行为建立起信心。然后,才能开始测试整个系统。

为什么要使用单元测试

  • 单元测试使工作完成的更轻松
  • 单元测试使你的设计更好
  • 大大减少花在调试上的时间
  • 能帮助你更好的理解代码

没有单元测试

  • 任何代码都是在假定其他代码是正确无误的情况下编写的。
  • 修改一处代码时无法得知会对其他代码产生怎样的影响。
  • 任何一处改动都需要进行功能级别的整体调试。

单元测试难以推动的原因

太花时间

很多人认为单元测试很花时间,但是想想我们在下面几点话的时间,我经常看到为了测试一个简单的API方法,我们很多人必须让前端跑起来,甚至自己写一个客户端才能调用

  • 调试上花的时间
  • 对自认为正确的代码,花了多少时间确认代码是正确的。
  • 定位Bug所耗的时间

测试不是我的工作

很多人认为测试不是自己的工作,但是想一想每次测试提出一个bug所花的时间,以及你改bug所化的时间,所以下面2点是很重要

  • 内在质量的重要性
  • 测试应该是辅助,好的软件是开发设计出来的,不是测试出来的

系统可测试性差

  • 系统耦合度很高,我们需要提高我们的团队的设计能力。

单元测试最佳实践

实践一: 三到五步

  • SetUp
  • 输入
  • 调用
  • 输出
  • TearDown

实践二: 运行快速

###为什么?
单元测试运行很频繁,是辅助开发的,在开发过程中运行,如果慢影响很大

多快较好?

  • 单个测试小于200ms
  • 单个测试套件小于10s
  • 整个测试小于10分钟

实践三:一致性

任何时候同样的输入需要同样的结果

    Date date=new Date()
    Random.next()

这样的代码都需要Mock掉,不然时间每次都不同,结果就会不一样。

实践四:原子性

** 所有的测试只有两种结果:成功和失败**
不能部分测试通过

实践五:单一职责

一个测试只验证一个行为

** 测试行为,不要测试方法 **

  • 一个方法,多个行为    —–>  多个测试
  • 一个行为,多个方法   —–   一个测试
    这里的一个行为,多个方法一般指这个方法调用private, protected, getters, setters
  • 多个Assert只有在测试同一个行为时可以接受

实践六:独立无耦合

单元测试之间无相互调用

  • 单元测试执行顺序无关
  • 不同的顺序无影响

单元测试之间不能共享状态

比如一个测试里设置了一个属性值,然后在另外一个测试里用,如果必须共享可以放到Setup里

实践七:隔离外部调用

  • 单元测试需要快速运行,且每次结果一致,所以需要隔离一切对外部的调用。
  • 不使用具体的其它真实类,就是不要new
  • 不读数据库
  • 不读网络
  • 不读外部文件
  • 适当时候可以构造一个相同的内部文件来Mock
  • 不依赖本地时间
  • 不依赖环境变量

实践八: 自描述

  • 单元测试是开发级文档
  • 单元测试是方法的描述

实践九: 单元测试逻辑

  • 单元测试必须容易读和理解的
  • 变量名,方法名,类名
  • 无条件语句,无Switch
    办法:分解if到多个测试,所有的输入都是已知的,所有的结果都是一定的(Mock)
  • 无循环语句
  • 无异常捕捉
    ** 测试预知的异常,用ExpectedException方法 **

实践十: 断言

  • 断言信息最好包含Business Information

  • 断言信息包含出错的具体信息如果失败

  • 适当时候可以封装自己的Assert
    比如:

    Assert.IsProgrammer(Jack)
    Return Jack. Cancooking() && Jack.CanCoding()

实践十一:产品代码

  • 产品代码无测试逻辑

不能有:

    If(global.IsTest){…}
  • 测试代码和产品代码要分离
  • 不要在产品代码里有任何只供测试用的代码
  • 使用依赖注入

最后,单元测试常用技术及工具

下面是.NET程序常用的单元测试需要的技术和工具,其它语言请自信比对。

  • 面向接口编程
  • 依赖注入(Castle, Unity, Ninject)
  • Moq
  • 测试工具(xUnit) 
  • .Net Nunit
  • 代码覆盖率测试工具Ncover
  • 自动运行测试辅助工具NCrunch
  

前端构建大法 Gulp 系列 (四):gulp实战

前面讲了很多理论,那么这一节我们将讲一些实战的例子

安装Node.js

先在命令行下输入 node -v 检查一下是否装了node, 如果没有请参考 https://github.com/joyent/node/wiki/Installing-Node.js-via-package-manager 安装

然后再用 npm -v 来确保Node.js 安装正确

安装 Gulp

我们可以使用npm来安排装Gulp, 为了可以在命令行全局使用,我们安装到全局,另外确保其它的程序员可以使用,我们保存到项目的package.json里

1
npm install gulp -g

创建项目

创建一个文件目录,然后建立对应的文件夹

  • src — 源文件:

    • images 
    • scripts 
    • styles 
  • build — 编译后文件输出到的生产文件夹:

    • images 
    • scripts 
    • styles 

我们先使用npm init来创建类似Nuget package的package.config一样的文件,这样我们就知道项目依赖哪些插件,而且我们不需要把插件提交到代码库,其它程序员只需要使用 npm install 就可以安装所有配置的插件

然后我们需要创建一个gulpfile.js文件,gulp默认是调用这个文件的。

我们在目录下使用

  npm install gulp --save-dev  # 这样可以把gulp安装到本地

使用插件

比如我们想检查我们的js文件,那么我们需要安装 gulp-jshint插件

1
npm install gulp-jshint --save-dev

然后添加一个test.js文件到src/scripts下,内容如下

1
2
3
4
5
var hi="hello"

function sayHello(){
console.log("Jack "+hi)
}

jshint 代码检查

然后我们修改gulpfile.js内容如下

1
2
3
4
5
6
7
8
9
10
11
12
// include gulp
var gulp = require('gulp');

// include plug-ins
var jshint = require('gulp-jshint');

// JS hint task
gulp.task('jshint', function() {
gulp.src('./src/scripts/*.js')
.pipe(jshint())
.pipe(jshint.reporter('default'));
});

然后运行

gulp jshint

看控制台输出就知道我们少了分号。

代码合并压缩

我们新建一个 ./scripts/b.js, 然后我们把js文件合并然后压缩并输出到./build/scripts/all.js 下,同时移除debug信息

我们需要安装一下插件

1
2
3
npm install gulp-concat --save-dev 
npm install gulp-strip-debug --save-dev
npm install gulp-uglify --save-dev

修改gulpfile.js

1
2
3
4
5
6
7
8
9
10
11
12
var gulp = require('gulp'); 
var concat = require('gulp-concat');
var stripDebug = require('gulp-strip-debug');
var uglify = require('gulp-uglify');

gulp.task('scripts', function() {
gulp.src(['./src/scripts/*.js'])
.pipe(concat('all.js'))
.pipe(stripDebug())
.pipe(uglify())
.pipe(gulp.dest('./build/scripts/'));
});

我们看到gulp已经把我们文件合并了,移除了console.log, 而且进行了压缩。

至此,已经基本上知道gulp怎么使用了,下面展示一些其它的功能的代码

1
2
npm install gulp-autoprefixer --save-dev 
npm install gulp-minify-css --save-dev

示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var gulp = require('gulp'); 
var concat = require('gulp-concat');
var stripDebug = require('gulp-strip-debug');
var uglify = require('gulp-uglify');
var autoprefix = require('gulp-autoprefixer');
var minifyCSS = require('gulp-minify-css');

gulp.task('scripts', function() {
gulp.src(['./src/scripts/*.js'])
.pipe(concat('all.js'))
.pipe(stripDebug())
.pipe(uglify())
.pipe(gulp.dest('./build/scripts/'));
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// CSS concat, auto-prefix and minify
gulp.task('styles', function() {
gulp.src(['./src/styles/*.css'])
.pipe(concat('styles.css'))
.pipe(autoprefix('last 2 versions'))
.pipe(minifyCSS())
.pipe(gulp.dest('./build/styles/'));
});

// default gulp task
gulp.task('default', [ 'scripts', 'styles'], function() {

// watch for JS changes
gulp.watch('./src/scripts/*.js', function() {
gulp.run('jshint', 'scripts');
});
// watch for CSS changes
gulp.watch('./src/styles/*.css', function() {
gulp.run('styles');
});
});

至此,大家应该熟悉gulp的使用,尽情去挖掘gulp plugin的宝藏吧。

前端构建大法 Gulp 系列 (三):gulp的4个API 让你成为gulp专家

gulp 本身能做的事情非常少,主要是通过插件来提供各种功能,gulp本身只提供了4个非常简洁的API, 掌握这4个API你就基本掌握了gulp的全部。

一、gulp.task

gulp 是基于task的方式来运行

定义

gulp.task(name [, deps, fn])
注册一个task, name 是task的名字,deps是可选项,就是这个task依赖的tasks, fn是task要执行的函数

示例

1
2
3
4
5
6
7
gulp.task('js', ,['jscs', 'jshint'], function(){
return gulp
.src('./src/**/*.js')
.pipe(concat('alljs'))
.pipe(uglify())
.pipe(gulp.dest('./build/'));
});

提示

上例中

  • jscs和jshint先运行,随后再运行js的task.
  • jscs和jshint是并行执行的,而不是顺序执行

二、gulp.src

定义

gulp.src(globs[, options])

与globs 匹配的文件,可以是string或者一个数组

示例

1
2
3
4
5
6
7
8
9
gulp.src(['client/*.js', '!client/b*.js', 'client/c.js'])   # !是排除某些文件

gulp.task('js',['jscs', 'jshint'],function(){
return gulp
.src('./src/**/*.js', {base:'./src/'})
.pipe(uglify())
.pipe(gulp.dest('./build/'));

});

options.base 是指多少路径被保留,比如上面的 ./src/users/list.js 会被输出到 ./build/users/list.js

提示

如果我们需要文件保持顺序,那么出现在前面的文件就写在数组的前面

1
gulp.src(['client/baby.js', 'client/b*.js', 'client/c.js'])  

上面baby.js就出现在最上面。

三、 gulp.dest

定义

gulp.dest(path[, options]) 就是最终文件要输出的路径,options一般不用

四、gulp.watch

定义

gulp.watch(glob [, opts], tasks) or gulp.watch(glob [, opts, cb]) 就是监视文件的变化,然后运行指定的Tasks或者函数,这个相比Grunt需要使用插件,gulp本身就支持的很好。

示例

1
2
3
4
5
6
7
8
9
gulp.task('watch-js', function(){
gulp.watch('./src/**/*.js',['jshint','jscs']);
});

gulp.task('watch-less', function(){
gulp.watch('./src/**/*.less',function(event){
console.log('less event'+event.type+' '+event.path)
});
});

最后

gulp就是如此的简单,你只需要掌握这四个API就够了,剩下的就是熟悉相关的plugin了。

参考链接 https://github.com/gulpjs/gulp/blob/master/docs/API.md

前端构建大法 Gulp 系列 (二):为什么选择gulp

在上一篇 前端构建大法 Gulp 系列 (一):为什么需要前端构建 中,我们说了为什么需要前端构建,简单一句话,就是让我们的工作更有效率。

相信熟悉前端的人对Grunt一定不陌生,实际上我自己之前的很多项目也是在用Grunt, Grunt的出现是前端开发者的福音,大大减少了前端之前很多手工工作的繁琐以及我上一篇 前端构建大法 Gulp 系列 (一):为什么需要前端构建 提到的那些问题。

那么既然Grunt可以做到几乎所有的事情,那么为什么我们需要Gulp呢?

Grunt与Gulp的区别

我们来看一下一般前端构建的流程

二者处理流程的区别

Grunt 的方式

Gulp的方式

配置的简洁程度

Grunt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
module.exports = function(grunt) {

// Project configuration.
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
uglify: {
options: {
banner: '/*! <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> */\n'
} ,
build: {
src: 'src/<%= pkg.name %>.js',
dest: 'build/<%= pkg.name %>.min.js'
}
}
});

grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.registerTask('default', ['uglify']);
};

Gulp

1
2
3
4
5
6
7
8
gulp.task('default',function(){    
return gulp
.src("**/*.js")
.pipe(jshint())
.pipe(concat())
.pipe(uglify())
.pipe(gulp.dest('./build/'))
})

所以从上面的一些代码对比来看,Gulp明显比Grunt要简洁易用很多。

最后,总结一些 Grunt的一些问题

  • 配置过于复杂
  • 插件职责不单一 (就是不SRP)
  • 临时文件目录多
  • 性能慢 (因为临时文件多,自然读IO多)

下一篇我们将开始学习如何使用gulp来构建我们的前端。

敏捷实践系列(七):为什么你都听客户的,客户却不满意

开篇

这样的场景你是不是很熟悉?客户让你做一个软件,你需要他给你写出需求,当它给你写出需求后,在你认为时间非常紧的情况下,你辛辛苦苦,加班加点,费劲九牛二虎之力,最后赶在最后时刻给客户提交了,你满怀希望等待客户给你的表扬,你万分坚信领导对你的辛苦会给予高度认可和鼓励,你觉得很快就要带一朵“小红花”时,最后你得到的是绵绵无绝期的等待,甚至是客户的不满意,这是为什么呢?这种情况在我的团队里也会出现,有时候我让改一个东西,经常得到的回复就是:”客户就是这么要求的,而且描述很清晰,不能改!”, 最后如果不改的结果就是客户不满意。

为什么我们听客户的,客户却不满意?

客户如果说要一个 “杯子”, 我们是怎么做的?

我们的很多程序员一般是这么工作的,客户说我需要一个 “杯子”,然后程序员就在网上或者生活中搜索杯子,于是程序员就得到了很多杯子,现在各种各样的茶杯,各种各样的酒杯等等,这时候程序员就按自己的喜好选中了一个杯子,聪明一点的程序员可能会衡量一下制造不同杯子的时间成本,选定以后就开始进行模仿,最后你没日没夜,加班加点终于把杯子做出来了,客户会说:怎么会做一个这样的杯子要这么久时间? 因为市场上已经有人做出来过,所以客户就会觉得照抄应该很快,其次客户会觉得这个杯子不是他想要的,如果是抄一个别人一样的杯子,那为什么不直接买一个呢?

客户如果说要一个”杯子”, 他要的不是一个”杯子”

实际上,客户说要一个“杯子”, 他要的不是一个杯子,他要的不是用来装水的,这个就像我们对一个女孩说:“我想和你一起看明早的太阳”,我们都知道他要的不是白天而是夜晚,不是吗?

所以,当客户说要一个“杯子”时,他要的不是一个“杯子”,因为“杯子”只是一个名词,显然客户要的不是一个名词,而是一个”动词”, 所以我们要想到的是这个杯子用来干什么?我们脑子里要想是不是几个老人在喝茶?一对情侣在喝咖啡?一个婴儿要用来喝奶?还是单身狗要用这个“杯子”?

可见,当客户要一个“杯子”时,他要的是一种场景,一个动态的场景,一个有人使用的场景,这个场景就是用户体验,你想想老人喝茶的杯子和单身狗要用的杯子能一样吗?

要自信的对客户说 “NO”

这个还是那句话,就是“为什么你都听客户的, 客户却不满意?” 我举个简单的例子,你生病了,你到医院去看病,你告诉医生怎么给你治疗,有的人甚至是在网上查了查资料,或者曾经有别的医生给这个病开个药方,然后你给医生说你要吃什么药,还有的人拒绝血常规检查,最后他的病要么没好,要么拖的时间很长,最后你还对医生非常不满意。你想想你的客户给你提需求的时候,是不是有的时候就和这个病人一样?

为什么医生会对你说 “NO” ,甚至不管你了呢? 因为医生比你更知道怎么治疗,如果按你说的肯定是不行,那么回到程序开发,软件设计上,做一个程序员或者高级软件开发人员,为什么不能给客户说 “NO” 呢?因为我们比客户更知道怎么开发,我们比客户对软件更专业不是吗?

协助客户改进需求

当然,我们不能简单粗暴的对客户说 “NO”, 由于我们对软件更了解或者说更专业,我们就可以给客户一些更好的方案,由于客户对技术的不了解,经常会提出一些既耗时又非常“愚蠢”的方案,那我们需要利用我们的专业知识来告诉客户什么是更好更省时的方案,有的时候需求稍加改动会让客户体验更好,而且开发更快,我相信这样的场景在不同的项目里有很多。

最后

所以,当我们拿到客户需求的时候,我们不是卷起袖子就开始编码,而是第一步想想客户要的到底是什么?他说的真的是他想要的吗? 他想要的说了吗?我们需要知道客户的项目的远景是什么?我们需要站在整体的角度,站在更 High level的角度来看需求。

举个例子,我曾经做过的一个项目,客户说要做房间预订,还要分淡旺季,还要有Offer, 还有内部员工不同价,团队当时想的很复杂,想到了想艺龙那样的,但是当我去了客户那边了解后,他们只有5个公寓而已,而且只是给集团员工别的地方来的提供住宿从而计入别的分公司成本而已,我觉得hard code出来一个版本都够用好几年,而且这样的话,原来的估计至少得半年以上吧,当你清楚客户最终需要啥的时候最多两个礼拜就弄完了。

总之,听客户没错,但是无脑的听客户的,完全听客户的就有问题了,就像如果你完全听你媳妇的,然后她错了后,她会说:“我让你那样做,你就那样做吗?你自己脑子是干什么的!”

  

前端构建大法 Gulp 系列 (一):为什么需要前端构建

我们都知道使用IDE编写后端程序时,我们都需要Build, 对.NET来说,我们一般需要使用Visual Studio来确保我们的项目编译通过,而且项目编译通过是对所有程序员的基本要求。

但是,由于很多后端程序员对前端的很多东西不了解,导致在做WEB项目时出现了一些问题。

JavaScript和CSS的版本问题

我们都知道 JavaScript和CSS属于静态文件,如果地址不变,浏览器会缓存这些文件,那就意味着当我们需要改JavaScript或者CSS文件的时候,即使我们后端改了,那么客户端也是看不到,这个在“JS一统天下”的时代是不可接受的,因为现在几乎所有的WEB 程序都严重依赖JavaScript,而所有的网站都是需要使用CSS的。在我经历过的项目即使是很多年经验的程序员都出现过JavaScript和CSS文件的版本问题,比如客户让修复一个Bug,这个Bug是JavaScript引起的,程序员修复了,或者是客户说改一个背景颜色,可是当我们给客户部署后或者代码交给客户客户部署时,客户说Bug依然存在,这个时候程序员经常说的话就会出现了 “我本地是好的呀”,最后再找来别人帮忙后,发现原来是没有清除浏览器的缓存,于是有的程序员就赶紧给客户说:“你需要Ctrl+F5 清除浏览器的缓存”。 每当我听到这样的话时就像关上灯留给我一屋子黑,首先,有几个普通用户会使用Ctrl+F5? 其次,有几个用户愿意去Ctrl+F5?

那么怎么办?我想很多程序员都知道加一个版本号就可以了,这样浏览器就会认为是新的文件,比如原来是 http://www.a.com/app.js 你现在只需要把地址改为http://www.a.com/app.js?v=1.0 即可

但是如果这个动作是手动的,那么10次基本上至少有5次程序员会忘掉,那么这就是为什么我们需要前端构建

JavaScript和CSS的依赖问题

我们经常出现的另一个问题,就是JavaScript和CSS的依赖问题,说的通俗点就是JavaScript和CSS的在页面中的顺序问题!

我们经常发现CSS没起作用,JavaScript的某个变量和方法找不到,有很多情况都是因为引入JavaScript或者CSS的顺序不对,虽然我们可以使用一些RequireJS之类的模块管理,但是依然在很多情况下需要引入不同的文件,尤其是CSS没有一个好的模块化管理的组件。

那么我们就需要有一个统一的地方来管理JavaScript和CSS的顺序问题,而构建工具可以大大减少此类问题。

性能优化

我们都知道浏览器请求的文件越多越耗时,请求的文件越大越耗时,尤其是在我们现在很多使用前端MVC, MVVM框架的时候,我们为了前端代码更清晰,结构更合理,我们就由很多JS文件,无疑又拖慢了网页的速度。为了解决这个问题,因此我们需要做两件事

文件合并

浏览器需要下载多个JS文件,而浏览器是有并发限制,也就是同时并发只能下载几个文件,假如浏览器并发数是5,你有20个JS文件,而每5个需要2S, 那么你光下载JS文件都需要8S,那么网页的性能可想而知,所以我们需要合并多个文件以减少文件的数量。

文件压缩

我们知道文件越大,下载越慢,而针对JavaScript和CSS, 里面的空格,换行这些都是为了让我们读代码时更容易阅读,但是对机器来说,这些对它没有影响,所以为了减少文件大小,一般的情况我们都会用工具去掉空格和换行,有时候我们还会用比较短的变量名(记住这个要让工具最后压缩时做,而源代码一定要保证命名可读性) 来减少文件大小。

而所有的前端构建工具都具有文件合并和压缩的功能。

效率提升

Vendor前缀

在CSS3使用越来越多的时候,我们都知道一些CSS的特性,不同的浏览器CSS有不同的前缀,如果我们手工添加将会很繁琐,而如果使用构建工具,很多构建工具可以自动给我添加CSS的Vendor前缀

单元测试

JavaScript的单元测试在使用MVC或者MVVM的框架后,变得越来越容易,而单元测试是质量保证的一个很重要的手段,所以在提交之前,使用构建工具自动跑一遍我们的单元测试是非常重要的

代码分析

我们写的JavaScript很多时候会有一些潜在的bug, 比如忘了添加分号,某个变量没有等等,使用一些JavaScript的代码分析工具,可以很好的帮我们检查一些常见的问题。

HTML引用JavaScript或者CSS文件

比如我们需要使用Bower之类来引用前端JavaScript和CSS的第三方库,那么如果版本升级,添加移除等都用手工来修改HTML的话,第一比较耗时,第二比较容易疏漏,尤其是在我们需要切换Debug和production版本时将会有很多额外的工作,那么使用前端构建工具可以很好的解决这些问题。

最后

以上我只是列出了前端构建最常用的一些功能,我相信还可以发觉很多构建工具可以替代我们手工做的事,后面我将详细讲讲如何使用Gulp这个神器来一一解决我们上面提到的问题。

敏捷实践系列(六):Team Leader 你不再只是编码, 来炖一锅石头汤吧

为什么软件项目需要 Team Leader

多年以前,当我接触敏捷时,我接触到一个概念,叫做 “自组织的团队”,当时我看了一些估计从没做过敏捷的一些凭空捏造的很多文章(事实上,这类人现在越来越多),那些文章多见名猜意,提出了自组织的团队就是团队自己组织,不需要Leader, 一开始我也是这么认为,甚至想尽一切办法向这个方向靠拢,而且成功的提交了一些项目,甚至于连我自己都相信这是自组织的结果,但是后来一想,我在团队里的所想所做不正是一个Team Leader 需要做的吗?

自组织团队的形成需要一个过程,而且目标只能无限靠近,难以完全达到

就拿一个足球团队来说,就拿 “宇宙队” 巴萨来说,也是需要一个教练,同时也需要组建团队,买卖球员,队伍文化建设,战术打法等等,那么软件项目团队来说,自组织团队同样需要组建,文化建设,代码规范,Team Rule, 团队磨合,质量意识,技术交流,客户需求范围把控,会议组织,冲突解决,开发流程等等。

自我保护,害怕承担责任是大部分人的天性

在没有一个好的敏捷文化的公司,大部分的程序员都不愿意做更多的事情,话句话说,更愿意做明确分配的事情,因为做的事情越少,自然问题越少,那么责任就越少,这是一种本能的自我保护,这也是大部分人难以成长的重要原因,所以必须要一个人来承担更多的责任,当然方法可以是把要做的事情分的更细,更明确,最终每个人做的事情更细,更多,更明确,那么每一个人要承担的责任就更多了。

###被领导惯了

很多中国的孩子,尤其是很多现在正处于黄金时代的程序员,独立意识确实要差一些,从小被父母装在一个大 “笼子”里,比如去哪里都是大人在前面牵着后面的小孩,老师严格教条的作业却只有一个标准答案,甚至在我看了写错一个字要重写一百遍一样猪一样的惩罚还至今流传着,忘了教育的本质是要把字学会而不是把字写一百遍,等等类似的东西,使我们不敢去思考,习惯被别人领导。

比如,我爱爬山,和我一起爬山的大部分人去一个没有去过的地方,都喜欢走在我的背后,因为他对未知有恐惧!习惯别人牵着走。

这个问题,我以前以为对高智商的程序员来说应该很少,后来经过10几年,我发现这和其它行业的人一样多。

所以,如果没有Leader, 大家不知道怎么干! 没有Leader组织,大家不知道干什么?

软件团队Team Leader的诞生

由于上面的原因,我们需要一个Team Leader, 但是由于太多人习惯被领导,害怕承担更多责任,我们就急需要一个Team Leader, 但是一个好的Team Leader是非常难找的,因为一个好的Team Leader要做很多事情才能把一个Team变成好的Team.

不懂技术的Team Leader在软件项目里成功的概率很小

由于软件项目来说,一个不懂技术的人可以当一个Team Leader, 但是要想当一个的Team Leader是难于登天,因为如果你不懂技术,那问题太多了,你怎么知道大家的评估时间靠谱?你如何向客户展示你的方案,你的优势?由于文人相亲,有技术的人一般会鄙视不懂技术的人瞎指挥,从技术人员喜欢鄙视技术人员这点就不难想象的出来。

请不要举马云的例子,你啥时间看到马云去直接领导一个技术团队了.

不懂管理的Team Leader 也难以成为优秀的Team Leader

因为Team Leader难找,所以在中国又一个常见的事情不断上演,那就是 “学而优则仕”,同样这个在我的团队里也大量存在,这是没有办法的事情, 因为如果他不懂管理,他还至少是一个程序员,相比不懂技术的管理人来说,如果他管理做不好,那对公司就没有什么价值了。

学而优则仕

在软件团队里,我们都知道一个Leader懂技术是多么的重要,那么我们唯一的选择就是沿用了中国多年的传统,那就是“学而优则仕”,比如上小学时,老师不都是让学霸的当班长吗? 所以很多技术还不错的人,都在团队需要的时候 “被挺身而出”,“被临危受命” 成为了Team Leader,但是这样出来的Team Leader 由于由于没有太多的管理项目的知识,没有团队管理的经验,往往也有不少问题,请继续往下看!

Team Leader 你不再只是编码,请炖一锅石头汤

由于“学而优则仕”,导致大部分软件团队Team Leader更多的专注于技术,就自然把更多的时间花在编码上,因为编码是立即可以看到的产出,而忽略了一个Team Leader要做的更重要的事情,比如团队文化建设,项目过程,质量保证,进度跟踪等等的事情。很多时候,我们缺少这些依然把项目做完了,但是实在很多加班,甚至是Team Leader卷起袖子一个定俩的情况下干完了,这样大部分情况就是客户感觉还OK, 但是难以达到满意。这还是自我保护的意识,害怕客户看不到自己实际的编码产出,实际上忽略了团队整体的目标的重要性。

关于技术团队Team Leader应该做什么,我本文就不想讲太多,有时间我会再写几篇关于Team Leader的文章,但是本文我强调的是技术团队的Team Leader不能只是编码,他要意识团队管理的重要性,哪怕这个“重要性”在别人看来什么都没有,不用害怕,因为我们只需要最终的项目成功来证明。

三言两语难以让大家明白Team Leader应该做什么,我就用一则寓言故事来告诉Team Leader 请炖一锅石头汤。

很多年前,有三个士兵,他们从战场回来既饥饿又疲倦,这时他们来到了一个小村庄。然而由于粮食遭遇欠收和连年的战争,村民们迅速的将它们的一小点粮食藏了起来,并在村子的广场中接待了士兵们,搓着双手,哀叹着他们是多么缺少食物。

士兵们平静地与村民们交谈着,第一个士兵对村庄的长老说道:“既然你们的土地收成不好,不能分给我们点吃的,那么我们将会与你们分享我们所有的:如何用石头做一道好汤的秘密。”

自然啦,村民们都十分好奇,很快他们就升起了火,架起了城里最大的一口锅,士兵们将三颗光滑的石子丢到了锅里。“这将是一锅好汤”第二个士兵说;“不过如果有一撮盐和一些欧芹那就更棒啦!”一个村民跳了起来,喊道“多幸运啊!我刚刚想起来家里还剩下些呢!”于是她跑回家,带着满满一围裙的欧芹和一根萝卜回来了。随着锅里的水渐渐煮沸,村民们的记忆力也变的越来越好,很快地,大麦,胡萝卜,牛肉还有奶油,统统被投入了这个大罐子里。

他们吃啊跳啊唱啊~直到深夜,美妙的宴会和新结交的朋友让每个人都感到焕然一新。当早上三个士兵醒来时,他们发现所有村民正站在他们面前。在他们脚边放着有一包这个村子最好的面包和奶酪。“你们把最好的礼物送给了我们:如何从石头里做汤的秘密”,一位长老说道,“这一点我们永远也不会忘记。”第三个士兵转身冲大伙说到:“这并没有什么秘密,但是有一件事是确定的:只有一起分享,我们才可能举办一次宴会。”说完,他们又踏上了路,慢慢走去了。

上面这个故事,我希望Team Leader能够明白虽然士兵并没有什么,但是他却让大家把好东西都拿出来一起做了一锅好烫,村民就是你的团队成员,一开始都把好东西藏起来不是吗?你需要做的就是拿出你的“秘密配方” 和大家一起炖上一个鲜美的石头汤吧!

  

一小时学会C# 6

c# 6已经出来有一段时间了,今天我们就详细地看一下这些新的特性。

一、字符串插值 (String Interpolation)

C# 6之前我们拼接字符串时需要这样

 var Name = "Jack";
 var results = "Hello" + Name;

或者

 var Name = "Jack";
 var results = string.Format("Hello {0}", Name);

但是C#6里我们就可以使用新的字符串插值特性

  var Name = "Jack";
  var results = $"Hello {Name}";

上面只是一个简单的例子,想想如果有多个值要替换的话,用C#6的这个新特性,代码就会大大减小,而且可读性比起之前大大增强

 Person p = new Person {FirstName = "Jack", LastName = "Wang", Age = 100};
 var results = string.Format("First Name: {0} LastName: {1} Age: { 2} ", p.FirstName, p.LastName, p.Age);

有了字符串插值后:

 var results = $"First Name: {p.FirstName} LastName: {p.LastName} Age: {p.Age}";

字符串插值不光是可以插简单的字符串,还可以直接插入代码

 Console.WriteLine($"Jack is saying { new Tools().SayHello() }");

 var info = $"Your discount is {await GetDiscount()}";

那么如何处理多语言呢?

我们可以使用 IFormattable

下面的代码如何实现多语言?

 Double remain = 2000.5; 
 var results= $"your money is {remain:C}";  

# 输出 your money is $2,000.50

使用IFormattable 多语言

class Program
{
    static void Main(string[] args)
    {

        Double remain = 2000.5; 

        var results= ChineseText($"your money is {remain:C}");

        Console.WriteLine(results);
        Console.Read();
    }

    public static string ChineseText(IFormattable formattable)
    {
        return formattable.ToString(null, new CultureInfo("zh-cn"));
    }
}

# 输出  your money is ¥2,000.50

二、空操作符 ( ?. )

C# 6添加了一个 ?. 操作符,当一个对象或者属性职为空时直接返回null, 就不再继续执行后面的代码,在之前我们的代码里经常出现 NullException, 所以我们就需要加很多Null的判断,比如

 if (user != null && user.Project != null && user.Project.Tasks != null && user.Project.Tasks.Count > 0)
 {
   Console.WriteLine(user.Project.Tasks.First().Name);
 }

现在我们可以不用写 IF 直接写成如下这样

Console.WriteLine(user?.Project?.Tasks?.First()?.Name);

这个?. 特性不光是可以用于取值,也可以用于方法调用,如果对象为空将不进行任何操作,下面的代码不会报错,也不会有任何输出。

class Program
{
    static void Main(string[] args)
    {
        User user = null;
        user?.SayHello();
        Console.Read();
    }
}

public class User
{
    public void SayHello()
    {
        Console.WriteLine("Ha Ha");
    }
}

还可以用于数组的索引器

class Program
{
    static void Main(string[] args)
    {
        User[] users = null;

        List<User> listUsers = null;

        // Console.WriteLine(users[1]?.Name); // 报错
        // Console.WriteLine(listUsers[1]?.Name); //报错

        Console.WriteLine(users?[1].Name); // 正常
        Console.WriteLine(listUsers?[1].Name); // 正常

        Console.ReadLine();
    }
}

注意: 上面的代码虽然可以让我们少些很多代码,而且也减少了空异常,但是我们却需要小心使用,因为有的时候我们确实是需要抛出空异常,那么使用这个特性反而隐藏了Bug

三、 NameOf

过去,我们有很多的地方需要些硬字符串,导致重构比较困难,而且一旦敲错字母很难察觉出来,比如

if (role == "admin")
{
}

WPF 也经常有这样的代码

public string Name
{
  get { return name; }
  set
  {
      name= value;
      RaisePropertyChanged("Name");
  }
}

现在有了C#6 NameOf后,我们可以这样

public string Name
{
  get { return name; }
  set
  {
      name= value;
      RaisePropertyChanged(NameOf(Name));
  }
}

static void Main(string[] args)
  {
      Console.WriteLine(nameof(User.Name)); //  output: Name
      Console.WriteLine(nameof(System.Linq)); // output: Linq
      Console.WriteLine(nameof(List<User>)); // output: List
      Console.ReadLine();
  }

注意: NameOf只会返回Member的字符串,如果前面有对象或者命名空间,NameOf只会返回 . 的最后一部分, 另外NameOf有很多情况是不支持的,比如方法,关键字,对象的实例以及字符串和表达式

四、在Catch和Finally里使用Await

在之前的版本里,C#开发团队认为在Catch和Finally里使用Await是不可能,而现在他们在C#6里实现了它。

  Resource res = null;
    try
    {
        res = await Resource.OpenAsync(); // You could always do this.  
    }
    catch (ResourceException e)
    {
        await Resource.LogAsync(res, e); // Now you can do this … 
    } 
    finally
    {
        if (res != null) await res.CloseAsync(); // … and this.
    }

五、表达式方法体

一句话的表达式可以直接写成箭头函数,而不再需要大括号

class Program
{
private static string SayHello() => “Hello World”;
private static string JackSayHello() => $”Jack {SayHello()}”;

1
2
3
4
5
6
7
8
    static void Main(string[] args)
{
Console.WriteLine(SayHello());
Console.WriteLine(JackSayHello());

Console.ReadLine();
}
}

六、自动属性初始化器

之前我们需要赋初始化值,一般需要这样

1
2
3
4
5
6
7
8
9
public class Person
{
public int Age { get; set; }

public Person()
{
Age = 100;
}
}

但是C# 6的新特性里我们这样赋值

1
2
3
4
public class Person
{
public int Age { get; set; } = 100;
}

七、只读自动属性

C# 1里我们可以这样实现只读属性

1
2
3
4
5
6
7
8
9
public class Person
{
private int age=100;

public int Age
{
get { return age; }
}
}

但是当我们有自动属性时,我们没办法实行只读属性,因为自动属性不支持readonly关键字,所以我们只能缩小访问权限

1
2
3
4
5
public class Person
{
public int Age { get; private set; }

}

但是 C#6里我们可以实现readonly的自动属性了

1
2
3
4
public class Person
{
public int Age { get; } = 100;
}

八、异常过滤器 Exception Filter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
static void Main(string[] args)
{

try
{
throw new ArgumentException("Age");
}
catch (ArgumentException argumentException) when( argumentException.Message.Equals("Name"))
{
throw new ArgumentException("Name Exception");

}

catch (ArgumentException argumentException) when( argumentException.Message.Equals("Age"))
{
throw new Exception("not handle");

}
catch (Exception e)
{

throw;
}
}

在之前,一种异常只能被Catch一次,现在有了Filter后可以对相同的异常进行过滤,至于有什么用,那就是见仁见智了,我觉得上面的例子,定义两个具体的异常 NameArgumentException 和AgeArgumentException代码更易读。

九、 Index 初始化器

这个主要是用在Dictionary上,至于有什么用,我目前没感觉到有一点用处,谁能知道很好的使用场景,欢迎补充:

1
2
3
4
5
6
7
8
9
10
11
12
var names = new Dictionary<int, string>
{
[1] = "Jack",
[2] = "Alex",
[3] = "Eric",
[4] = "Jo"
};

foreach (var item in names)
{
Console.WriteLine($"{item.Key} = {item.Value}");
}

十、using 静态类的方法可以使用 static using

这个功能在我看来,同样是很没有用的功能,也为去掉前缀有的时候我们不知道这个是来自哪里的,而且如果有一个同名方法不知道具体用哪个,当然经证实是使用类本身的覆盖,但是容易搞混不是吗?

1
2
3
4
5
6
7
8
9
10
11
12
using System;
using static System.Math;
namespace CSharp6NewFeatures
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine(Log10(5)+PI);
}
}
}

总结

上面一到八我认为都是比较有用的新特性,后面的几个我觉得用处不大,当然如果找到合适的使用场景应该有用,欢迎大家补充。

最后,祝大家编程愉快。

微软程序员最好的时代来了

每过一段时间就有人跳出来说微软不行了,.NET不行了,然后就去舔Java, 但是一直让我觉得比较奇怪的是,几年以后那些人还在用.NET,而且继续喷着.NET, 舔着JAVA, 在我看来,这些人和那些天天喷自己的公司,却依然在那个公司,天天喷中国,却依然在中国的那些人是一样的。

语言只是工具

因为我不是非常熟习JAVA, 所以我不知道JAVA擅长做什么,但是我觉得.NET能做的事,基本上JAVA应该都能做,就像我认为JAVA能做的事.NET基本也都能做一样。但是奇怪的是我经常看到的是.NET人员喷.NET, 很少听到JAVA人员喷.NET, 不过我估计很多JAVA人员应该也忙着去喷JAVA吧。这个其实和语言没关系,只是和人的心理有关系,因为人总觉得“碗里的没锅里的好”。

语言之争已经持续很多年了,其实这个大家都知道没什么意义,网上不是流传一个语言的鄙视链吗?我觉得大家把他当个玩笑罢了,千万别当真,因为不管怎么鄙视,我们毕竟要吃饭的。

本来我也觉得写这篇文章没什么意义,但是看到那些喷.NET文章,基本上句句说的都没理,但是却可能对初入行的人带来很大的误解,就像骗子很容易骗小孩,因为小孩涉世未深。

我做开发已经10多年了,学过很多语言,但是真正用来吃饭的最主要的语言其实是Visual Basic和.NET, 当然还有”汉语”. 当初也学过Java, JSP, SSH. 但是最后选择了.NET,为什么,一是因为.NET技术真的入门很快,当年入门快的好处之一就是你立马就有一定的生产力,也就是你能很快开始干活,这样就有公司愿意给你付薪水了,也就意味着你可以更早从父母那里断奶了,另外一个原因就是当时面试的要.NET比要JAVA的多呀。

C# 语言

我学习过C, Java,Visual Basic, Ruby,Node.js等,但是我还是觉得C#是生产力非常高的一门语言,比如一些非常优秀的语言特性,你刚刚才能从其它语言里看到一点点,比如自动属性,LINQ, Lamda表达式,Action等,另外C#对多线程的封装让我们在多线程编程时极其方便,比如TPL. 还有令大家头疼的异步回调的问题,C#用非常优雅的Async, Await来解决,我们看到ES7里面已经开始实现类似的东西,是不是借鉴了C#呢?

多语言混用

对一个稍微复杂一点的程序来说,我们为什么一定要只使用一个语言呢?比如我们前端可以使用ASP.NET MVC, 后端可以使用Java 甚至是别的任何一个语言,服务我们可以使用WCF, 搜索我们可以使用Solar等等,我们甚至是WEB层,业务层都是用.NET, 而数据库可以使用MySQL或者MongoDB.

.NET或者JAVA只是系统的一部分

我们知道一个WEB程序,除了后端以外,很多其它的东西比如HTML, CSS,JavaScript,数据库这些不管你做Java还是.NET都是一样需要的。也就是前端技术都是相同,另外HTTP协议,TCP/IP这些也不分语言吧。不管你是JAVA还是.NET, AngularJS, ReactJS, HTML5, Bootstrap, Bower, Grunt, Gulp这些东西对你都是一样的吧?

微软技术能做什么

我没有做过JAVA程序,但是我使用微软技术10多年了,我就说一说微软技术能做什么。

桌面程序

Visual Basic

当年我毕业没多久,就加入一家马来西亚在中国的软件公司,这个公司主要是做门票系统,当时我们选择了Visual Basic, 做过VB的人都知道VB是多么的强大,除了极其方便的可见即所得的Form外,而且有几乎一切你想要的组件,另外我们都知道Windows是桌面系统中当之无愧的的霸主, 我不知道JAVA在这方面的优势是什么,如果有些人说要跨平台,但是我做了这么多年的企业软件,没有几个企业软件是需要跨平台的。当时在这个公司里做了售票系统,还有闸机系统,POS系统,我估计很多人不知道闸机系统原来用Visual Basic也可以做。广州,北京最大的游乐园都是我们当年做的,而当年整个乐园的信息系统都是使用的微软的技术。

WPF

微软推出的WPF, 我觉得是对桌面开发程序的极大的进步,几乎所有的桌面程序的展示都可以使用WPF来呈现,而通过WPF可以让我们非常方便快速的做出非常炫的桌面程序,WPF的XAML方式给了我们非常方便的写桌面窗体。 同时模板,动画等在WPF都是极其容易实现,另外MVVM在WPF的应用里非常盛行,WPF可是比Angular早出现了很多年。

UWP

不久前,微软推出了Windows 10, 这使一套系统可以运行在桌面,平板以及手机上,而我们可以使用XAML,甚至是JavaScript来开发一套程序就可以运行在多个终端,这个对个人用户可能没有什么,但是对企业用户太重要了,具体的细节我觉得大家可以去看一下MSDN或者Channel9

WEB

ASP.NET WEB Forms

我觉得很多人对微软的误解可能主要是这一部分,当年的ASP.NET WEB Forms由于是快速拖控件,导致界面生成很多难以认识的代码,大量的ViewState等,但是这并不能说ASP.NET WEB Forms不好,第一我们可以使用ASP.NET WEB Forms快速完成一些小型的WEB程序,加上有大量的第三方控件,是开发一些常规的程序快如闪电。第二我们可以尽量使用客户端的控件比如Repeater等,现在仍然有很多网站都是ASP.NET WEB Forms做的,难道这些网站都没有用户?!

ASP.NET WEB MVC

由于很多人对ASP.NET WEB Forms充满抱怨,而且WEB开发技术不断地革新,微软也顺应潮流及时的推出了ASP.NET MVC, 使用MVC可以写出非常清爽的代码,我们团队从ASP.NET MVC1.0 beta就开始使用,由于我也学习过Ruby On Rails,基本上这两个框架非常相似,极其方便的路由管理,View, Controller, Model的分层,使我们可以很好的使用强类型类开发,我们已经使用ASP.NET MVC成果提交过很多项目,还从来没有遇到过问题是出自.NET本身的。

WEB 服务

WCF

使用统一的模型,让我们开发WEB服务极其方便,大部分情况下我们只需要定义一个接口,配置相关的Binding和EndPoint就可以了,可以很方便的使用Http, TCP, Https,可以非常方便的使用各种安全策略,而这些就只需要简简单单的几行配置而已,WCF极大的简化了WEB服务的开发。

ASP.NET WEB API

除了WCF, 微软又推出了ASP.NET WEB API, 使得我们开发轻量级的WEB 服务极其容易,使用ASP.NET WEB API 我们可以非常容易实现服务的Restful. 而使用OWEN我们可以以任何方式来部署我们的API。

Azure 微软云

微软的云非常的强大,使用过微软云的都知道,微软云几乎可以满足我们一切对程序的要求,我们可以使用Azure web apps很方便的创建一个WEB, Webjob, 我们可以使用Azure SQL, 使用Azure storage, 可以极其方便的使用云的Queue, Bus等等,而且云集成了很多优秀的第三方程序,比如我们可以使用Redis作为Cache. 另外微软的Cloud Services让我们可以极其方便的管理我们的部署。这让我们很多程序员从一个程序员可以快速成为可以运维企业整套系统的人,而你只需要学习一些Azure的管理知识,比如自动扩展等等,而这些在云里面都可以快速简单的配置。我们几乎一半客户的系统都运行在Azure里面,而且我们可以一键部署,如果有问题,我们甚至可以使用Visual Studio来直接Debug云里面的网站。

移动开发

由于Mono,现在可以使用.NET来开发移动的应用程序,使用Xamarin可以使用C#开发出和原生性能一模一样的iOS和Android程序,我也熟悉Objective-C, 而且用Objective-C开发过程序,虽然说Objective-C 在某些方面很优秀,但是开发效率实在不敢恭维,不然Apple也不会推出Swift来革自己的命,另外当你既需要iOS,又需要Android的时候,你至少需要一个熟悉Objective-C的,又需要一个熟悉Java. 而同样地东西需要做两遍。而使用Xamarin做企业级APP,大量的业务逻辑可以共享,更别说可以直接使用效率非常的C#语言了,我们已经成功提交了好几个基于Xamarin的程序。

另外,Xamarin推出的Xamarin.Forms可以使用XAML及C#用一套代码来开发iOS, Android以及Windows Phone的程序,而且性能和原生的一模一样,我们也顺利开发过基于Xamarin.Forms的程序。

游戏开发

现在已经有很多程序基于Unity开发,而你可以选择C#来作为基于Unity的游戏的主要语言。可以搜索一下,已经有大量的上架游戏是使用.NET开发的。

小结

当然,以上只是我使用的微软相关技术,目前来说,除了客户明确选择其它语言以外,还真的没有多少是Java能做而.NET做不了的。

桌面,WEB, 移动开发 都可以使用.NET,简单点说,就是你都可以使用一门语言,那就是C#来开发

开发工具

Visual Studio

做.NET开发,配套的开发工具是Visual Studio, 我觉得Visual Studio是最好的IDE之一,你几乎可以使用做任何语言的开发,这个使用过的人都非常清楚,虽然说Vim是编辑器之神,而Emacs是神的编辑器,但是显示世界能有多少个神?

Resharper

Resharper是每个.NET程序员的必备工具之一,基本上可以让我们的开发效率提高三分之一,设个谁用过谁知道。

领域驱动设计

如果我没有记错的话,虽然Eric Evans较早写了领域驱动设计一书,但真正推动领域驱动设计的是有很多做.NET开发的,比如Greg Young, 我们可以看看NServicebus. 可以看看 NServicebus

我们已经使用领域驱动设计提交了一个非常大型的项目,这个项目是一个世界500强的主要系统。而这个系统就是使用的.NET C#,使用了CQRS, NServicebus, ASP.NET MVC, ASP.NET WEB API,SQL Server等等,系统已经运行了好几年了,还没有发现什么问题。

有兴趣的可以关注一下我的领域驱动系列。

长尾理论

很多人说大型的一些系统都没有用.NET, 比如BAT, 比如新浪微博,他们是不是一点都没有用.NET我不知道。我想说的第一那些系统都比较庞大,使用JAVA或者PHP很多时候是基于历史的选择,另外这些系统就那么几个,而且并不是所有的软件都是电商,都是微博?

我在这里想说的长尾,就是第一中小企业几乎占据所有的企业的80%, 而这些企业需要各种各样的系统,而这些企业不论是国内还是国外,都是Windows占大部分。虽然我也非常喜欢苹果的产品,但是企业是需要赚钱的。 也就是说80%的企业都不会像BAT那么大,那么至少这80%的系统使用.NET开发没有任何问题,再加上.NET有着很高的开发效率,我们有什么理由不选择呢?

使用.NET应该是企业或者客户项目的第一选择

如果只做WEB系统,或者只做电商之类,那么使用其它任何语言都没问题,但是一旦做企业系统,往往.NET是一开始非常安全的选择,为什么? 因为很多企业使用的系统是Windows, 使用的办公软件是Office, 使用的服务器是Windows Server,使用的是AD, 使用的邮件系统是Exchang Server, 我不知道你们使用JAVA和PHP和这些系统集成时是否方便,但是使用.NET是非常方便的。目前来看,.NET几乎可以满足企业应用的所有的现有的需求以及潜在的需求。

关于开源,关于免费

现在.NET很多东西都开源了,.NET CORE 和 ASP.NET VNext已经可以跑在Mac和Linux上了,我相信会越来越多的.NET程序将来会跑在Linux服务器上,另外大部分程序根本就用不了那么多服务器,如果我们真的需要那么多服务,证明公司已经很有钱了,还买不起几个Windows?而且如果真的需要那么多服务器,我们可以使用Microsoft Azure, 买几个Windows总比要请几个Linux运维工程师要便宜很多吧。

最后,没有人限制你只会.NET

没有谁能限制.NET程序员学习其他的语言,.NET程序员可以学习Java, Ruby, Node.js, 可以学习Event Driven, Message Queue, Solar, 学习MongoDB, Redis, 学习分布式缓存,学习任何其它语言需要学习的东西。

关于薪水

我不相信一个人学两个月JAVA, 不学习其它东西就可以立马成为一个优秀的程序员,就可以拿到很高的薪水,因为在我看来,要成为一个优秀的.NET程序员,需要学习大量的知识,我相信JAVA程序员也是一样。如果说.NET程序员年薪几百万我没见过,但是把.NET学好可以拿到相对不错的薪水还是没有问题的。

我们就是使用.NET的技术,如果你觉得你.NET技术还可以而没有地方发挥的,欢迎联系我 wangdeshui@outlook.com