函数式编程与面向对象编程[2]: 静态类型语言的表达力 静态类型语言与动态类型语言

函数式编程与面向对象编程[2]: 静态类型语言的表达力 静态类型语言与动态类型语言

之剑java

2016.5.3 21:43:20python


像Java或者C#这样强类型的准静态语言在实现复杂的业务逻辑、开发大型商业系统、以及那些生命周期很长的应用中也有着很是强的优点git

下面咱们就来学习一下这些知识.程序员

有三个名词容易混淆:github

  • Dynamic Programming Language (动态语言或动态编程语言)web

  • Dynamically Typed Language (动态类型语言)spring

  • Statically Typed Language (静态类型语言)编程

先定义一下标准:json

强类型语言(静态类型语言)

是指须要进行变量/对象类型声明的语言,通常状况下须要编译执行。例如C/C++/Java/C#api

弱类型语言(动态类型语言)

是指不须要进行变量/对象类型声明的语言,通常状况下不须要编译(但也有编译型的)。例如PHP/ASP/Ruby/Python/Perl/ABAP/SQL/JavaScript/Unix Shell等等。

1 静态类型语言

静态类型语言的类型判断是在运行前判断(如编译阶段),好比C#、java就是静态类型语言,静态类型语言为了达到多态会采起一些类型鉴别手段,如继承、接口,而动态类型语言却不须要,因此通常动态语言都会采用dynamic typing,常出现于脚本语言中.

不过,是否是动态类型语言与这门语言是否是类型安全的彻底不相干的,不要将它们联系在一块儿!

没有单元测试或者单元测试没有达到语句覆盖或者更强的弱条件组合覆盖,从而致使某些非正常流程发生时,流经这些未被测试的语句致使语法错误而最终整个程序都挂掉.对于业务系统来讲,这是很是严重的事情。

1.1 优势

静态类型语言的主要优势在于其结构很是规范,便于调试,方便类型安全

如今有这样一种趋势,那就是合并动态类型与静态类型在一种语言中,这样能够在必要的时候取长补短(下面在第4节中:在Scala语言的特点时介绍).

如今开发效率比之前高多了,主要缘由是由于开发语言和编译器的进步,这个趋势,只会继续下去,不要抱着过去的教条不放,java也是在不断改进的,加了reflection, 加了assert,加了泛型,下个版本,也要加脚本支持了。

其实静态类型语言,除了性能方面的考量以外,最大的优点就是能够提供静态类型安全,编译器能够检查你的每个函数调用是否是书写了正确的名字,是否是提供了正确类型的参数。这样一个系统,配合自定义类型的功能,可让不少错误(比许多人想象的要多)在编译时就能被发现和定位。

1.2 缺点

缺点是为此须要写更多的类型相关代码,致使不便于阅读、不清晰明了。

2 动态类型语言

所谓的动态类型语言,意思就是类型的检查是在运行时作的,好比以下代码是否是合法的要到运行时才判断(注意是运行时的类型判断):

<img src="http://static.oschina.net/uploads/img/201412/23070943_OfhR.jpg">

def sum(a, b):
return a + b

2.1 优势

动态类型语言的优势在于方便阅读,不须要写很是多的类型相关的代码;动态语言表明着更快更简单的技术大趋势,所以它将必然成为将来构建软件和互联网技术的主角。

动态语言足够灵活,所以虽然它可以让人更集中精力思考业务逻辑的实现,同时也向人工智能的方向走得更近一些,但所以它也更依赖于开发人员自己的技术功底,初学者、中级开发者,难以很好的利用它。 而静态类型语言,与咱们计算机教学的基本科目(c/pascal/basic)延续性比较好,因此对于刚毕业的学生而言,更好接受和学习。

2.2 缺点

缺点天然就是不方便调试,命名不规范时会形成读不懂,不利于理解等。

3 动态类型语言的表达力

动态语言一般更方便开发较小的项目,由于能够无需声明类型而节省了不少麻烦。另一个答案是,动态类型解除了程序员的束缚,能够最大的 发挥程序员的编程技能,能最有效的利用编程语言里的各类特征和模式。但这些能力都是一把双刃剑,更多的依赖于程序员的我的才能,若是用很差,或用的过分, 都会产生负面的害处。

  • 观点一:静态类型语言由于类型强制声明,因此IDE能够作到很好的代码感知能力,由于有IDE的撑腰,因此开发大型系统,复杂系统比较有保障。

对于像Java来讲,IDEA/Eclipse确实在代码感知能力上面已经很是强了,这无疑可以增长对大型系统复杂系统的掌控能力。可是除了Java拥有这么强的IDE武器以外,彷佛其余语言历来没有这么强的IDE。C#的Visual Studio在GUI开发方面和Wizard方面很强,可是代码感知能力上和Eclipse差的不是一点半点。至于Visual C++根本就是一个编译器而已,更不要说那么多C/C++开发人员都是操起vi吭哧吭哧写了几十万行代码呢。特别是像Linux Kernel这种几百万行代码,也就是用vi写出来的阿,够复杂,够大型,够长生命周期。

  • 观点二:静态语言相对比较封闭的特色,使得第三方开发包对代码的侵害性能够降到很低。

也就是说静态类型语言能够保障package的命名空间分割,从而避免命名冲突,代码的良好隔离性。可是这个观点也缺少说服力。

静态类型语言中C,VB都缺少良好的命名空间分割,容易产生冲突,可是并无影响他们作出来的系统就不够大,不够复杂。

而动态类型语言中Ruby/Python/Perl都有比较好的命名空间,特别是Python和Perl,例如CPAN上面的第三方库成吨成吨的,也历来没有据说什么冲突的问题。

诚然像PHP,JavaScript这样缺少命名空间的动态语言很容易出现问题,可是这彷佛是由于他们缺少OO机制致使的,而不是由于他们动态类型致使的吧?

说到大型系统,复杂业务逻辑系统,Google公司不少东西都是用python开发的,这也证实了动态类型语言并不是不能作大型的复杂的系统。其实我我的认为:

动态类型语言,特别是高级动态类型语言,反而可以让人们不须要分心去考虑程序编程问题,而集中精力思考业务逻辑实现,即思考过程即实现过程,用DSL描述问题的过程就是编程的过程,这方面像Unix Shell,ruby,SQL,甚至PHP都是相应领域当之无愧的DSL语言。而显然静态类型语言基本都不知足这个要求。

那静态类型语言的优点到底是什么呢?我认为就是执行效率很是高。因此但凡须要关注执行性能的地方就得用静态类型语言。其余方面彷佛没有什么特别的优点。

4 Java语言的问题

4.1 生产力问题

  开发调试慢,总体解决方案复杂。这只是相对而言,对于熟练的开发这也许不是问题,做为企业级解决方案而言,Java语言确实比较繁重,即便依赖了不少自动编译、动态加载、打包并部署的持续集成工具,调试速度依然很慢,与如今的快节奏的开发要求有明显矛盾. 不过, Java语言的生态不只仅是语言, 而是JVM. 因此java语言只是JVM生态的一个语言.后来者, 有Scala, Groovy, Fantom, Clojure, Ceylon, Kotlin 和Xtend–mostly等.
  

  

4.2 表达力问题

  整体来讲Java语言的编写过程更倾向于过程式的开发,在上一层面上封装了面向对象的特征和行为,语言的设计是上个世纪九十年代的风格,不是说语言自己很差是其抽象能力不够,即便到了Java8也只是对Lambda表达式进行了支持,所以引入了Functional Interface也即只有一个方法的接口,和接口里边的带具体实现的方法(为了兼容之前的代码不得不做出的让步)。Java语言发展到如今其语言特性庞大,若是要彻底了解须要几百页的文档,在其发展过程当中又只作加法没又减法,语言慢慢风格混杂,变成了如今这种四不像的状态,函数式的特性硬生生的嫁接在原来的面向对象特性之上。
  
    一段Java 实体类代码

  
  

package com.femon.entity;

import java.util.Date;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

/**
 * @author 一剑 2015年12月23日 下午4:10:18
 */
@Entity
@Table(name = "cm_service")
public class Service {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private int id;

    private String name;
    private String host;
    private String requestUrl;
    private String responseBody;
    private String expect;
    private String method;
    private int paramsType;// 1 : 普通参数请求串query str
    // 2 : header参数
    // 3: 表单方式提交参数
    // 4: json post请求
    private String paramsMap;
    private String platform;// H5 Web App

    private int todaySuccessTimes;
    private int todayFailTimes;
    /**
     * 0 失败 1 正常
     */
    private int state;

    private int totalSuccessTimes;
    private int totalFailTimes;
    private Date gmtCreate;
    private Date gmtModify;

    /**
     * platform.
     * 
     * @return the platform
     * @since JDK 1.7
     */
    public String getPlatform() {
        return platform;
    }

    /**
     * platform.
     * 
     * @param platform
     *            the platform to set
     * @since JDK 1.7
     */
    public void setPlatform(String platform) {
        this.platform = platform;
    }

    public String getParamsMap() {
        return paramsMap;
    }

    public void setParamsMap(String paramsMap) {
        this.paramsMap = paramsMap;
    }

    public Date getGmtCreate() {
        return gmtCreate;
    }

    public void setGmtCreate(Date gmtCreate) {
        this.gmtCreate = gmtCreate;
    }

    public Date getGmtModify() {
        return gmtModify;
    }

    public void setGmtModify(Date gmtModify) {
        this.gmtModify = gmtModify;
    }

    public int getTodaySuccessTimes() {
        return todaySuccessTimes;
    }

    public void setTodaySuccessTimes(int todaySuccessTimes) {
        this.todaySuccessTimes = todaySuccessTimes;
    }

    public int getTodayFailTimes() {
        return todayFailTimes;
    }

    public void setTodayFailTimes(int todayFailTimes) {
        this.todayFailTimes = todayFailTimes;
    }

    public int getTotalSuccessTimes() {
        return totalSuccessTimes;
    }

    public void setTotalSuccessTimes(int totalSuccessTimes) {
        this.totalSuccessTimes = totalSuccessTimes;
    }

    public int getTotalFailTimes() {
        return totalFailTimes;
    }

    public void setTotalFailTimes(int totalFailTimes) {
        this.totalFailTimes = totalFailTimes;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getHost() {
        return host;
    }

    public void setHost(String host) {
        this.host = host;
    }

    public String getRequestUrl() {
        return requestUrl;
    }

    public void setRequestUrl(String requestUrl) {
        this.requestUrl = requestUrl;
    }

    public String getResponseBody() {
        return responseBody;
    }

    public void setResponseBody(String responseBody) {
        this.responseBody = responseBody;
    }

    public int getState() {
        return state;
    }

    public void setState(int state) {
        this.state = state;
    }

    public String getExpect() {
        return expect;
    }

    public void setExpect(String expect) {
        this.expect = expect;
    }

    public String getMethod() {
        return method;
    }

    public void setMethod(String method) {
        this.method = method;
    }

    public int getParamsType() {
        return paramsType;
    }

    public void setParamsType(int paramsType) {
        this.paramsType = paramsType;
    }

    /**
     * TODO
     * 
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        return "Service [id=" + id + ", name=" + name + ", host=" + host + ", requestUrl=" + requestUrl
                + ", responseBody=" + responseBody + ", expect=" + expect + ", method=" + method + ", paramsType="
                + paramsType + ", paramsMap=" + paramsMap + ", platform=" + platform + ", todaySuccessTimes="
                + todaySuccessTimes + ", todayFailTimes=" + todayFailTimes + ", state=" + state + ", totalSuccessTimes="
                + totalSuccessTimes + ", totalFailTimes=" + totalFailTimes + ", gmtCreate=" + gmtCreate + ", gmtModify="
                + gmtModify + "]";
    }}

    
而其实,咱们早就知道,虽然这些 getter,setter都是模板化的东西, 能够自动生成的,可是这么冗余的代码,看起来仍是不爽!那些写Java代码的牛逼程序员老鸟们里面有一个叫Martin Odersky的, 就整了一个基于JVM的支持函数式又无缝融合OOP的程序设计语言Scala. 这样的实体类代码,在Scala中写做以下:
  

package com.steda.entity

import java.util.Date
import javax.persistence.{Entity, GeneratedValue, GenerationType, Id}

import scala.beans.BeanProperty

@Entity
class TedaCase {
  @Id
  @GeneratedValue(strategy = GenerationType.AUTO)
  @BeanProperty
  var id: Long = _
  @BeanProperty
  var name: String = _
  @BeanProperty
  var interfaceId: Long = _
  @BeanProperty
  var paramJsonStr: String = _
  @BeanProperty
  var expectOutput: String = _
  @BeanProperty
  var actualOutput: String = _
  @BeanProperty
  var dataSourceId: Long = _
  @BeanProperty
  var clearSql: String = _
  @BeanProperty
  var tddlApp: String = _
  @BeanProperty
  var state: Integer = _
  @BeanProperty
  var runTimes: Integer = _
  @BeanProperty
  var owner: String = _
  @BeanProperty
  var gmtCreate: Date = _
  @BeanProperty
  var gmtModify: Date = _
}

 咱们再看一个Controller层的写做, Scala能够与Java生态中优秀的框架无缝融合, 好比Spring,Junit. 尤为当今发展势头正猛的SpringBoot, JPA等框架, 更是大大的提高了开发测试的生产力.

<pre>

package com.steda.controller

import java.util.Date

import com.steda.dao.{DataSourceDao, TedaCaseDao}
import com.steda.entity.TedaCase
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.ui.Model
import org.springframework.util.StringUtils
import org.springframework.web.bind.annotation._
import org.springframework.web.servlet.ModelAndView

@RestController
@RequestMapping(Array("/tedacase"))
class TedaCaseController @Autowired()(

private val tedaCaseDao: TedaCaseDao,
                                   private val dataSourceDao: DataSourceDao) {

@RequestMapping(Array("/newPage/{interfaceId}"))
def goNewPage(@PathVariable(value = "interfaceId") interfaceId: Long, model: Model): ModelAndView = {

model.addAttribute("dataSources", dataSourceDao.findAll())
model.addAttribute("interfaceId", interfaceId)
new ModelAndView("/tedacase/new")

}

@RequestMapping(Array("/editPage/{id}"))
def goEditPage(model: Model, @PathVariable(value = "id") id: Long): ModelAndView = {

val tedacase = tedaCaseDao.findOne(id)
model.addAttribute("tedacase", tedacase)
model.addAttribute("dataSources", dataSourceDao.findAll())
new ModelAndView("/tedacase/edit")

}

@RequestMapping(Array("/detailPage/{id}"))
def goDetailPage(model: Model, @PathVariable(value = "id") id: Long): ModelAndView = {

val tedacase = tedaCaseDao.findOne(id)
model.addAttribute("tedacase", tedacase)
new ModelAndView("/tedacase/detail")

}

@RequestMapping(value = {

Array("", "/")

}, method = Array(RequestMethod.GET))
def list(model: Model, @RequestParam(value = "tedaCaseName", required = false) tedaCaseName: String): ModelAndView = {

var tedaCases: java.util.List[TedaCase] = new java.util.ArrayList[TedaCase]
if (!StringUtils.isEmpty(tedaCaseName)) {
  tedaCases = tedaCaseDao.findByName(tedaCaseName)
} else {
  tedaCases = tedaCaseDao.findAll()
}
model.addAttribute("tedaCases", tedaCases)
model.addAttribute("tedaCaseName", tedaCaseName)
new ModelAndView("/tedacase/list")

}

@RequestMapping(value = Array("/postnew"), method = Array(RequestMethod.POST))
@ResponseBody
def newOne(@RequestParam(value = "name") name: String, @RequestParam(value = "interfaceId") interfaceId: Long,@RequestParam(value = "paramJsonStr") paramJsonStr: String,@RequestParam(value = "expectOutput") expectOutput: String, @RequestParam(value = "owner") owner: String,@RequestParam(value = "clearSql") clearSql: String, @RequestParam(value = "dataSourceId") dataSourceId: Long, @RequestParam(value = "tddlApp") tddlApp: String) = {

val teda = new TedaCase()
teda.clearSql = clearSql
teda.dataSourceId = dataSourceId
teda.interfaceId = interfaceId
teda.tddlApp = tddlApp
teda.expectOutput = expectOutput
teda.owner = owner
teda.paramJsonStr = paramJsonStr
teda.name = name
teda.state = -1 // -1 未执行 0 失败 1 成功
teda.runTimes = 0
teda.gmtCreate = new Date()
teda.gmtModify = new Date()
tedaCaseDao.save(teda)

}

@RequestMapping(value = Array("/postedit"), method = Array(RequestMethod.POST))
@ResponseBody
def editOne(@RequestParam(value = "id") id: Long, @RequestParam(value = "name") name: String,@RequestParam(value = "paramJsonStr") paramJsonStr: String,@RequestParam(value = "expectOutput") expectOutput: String, @RequestParam(value = "owner") owner: String,@RequestParam(value = "clearSql") clearSql: String, @RequestParam(value = "dataSourceId") dataSourceId: Long, @RequestParam(value = "tddlApp") tddlApp: String) = {

val teda = tedaCaseDao.findOne(id)
teda.clearSql = clearSql
teda.dataSourceId = dataSourceId
teda.tddlApp = tddlApp
teda.expectOutput = expectOutput
teda.owner = owner
teda.paramJsonStr = paramJsonStr
teda.name = name
teda.gmtModify = new Date()
tedaCaseDao.save(teda)

}

}

</pre>

4.3 资源消耗问题

  Java语言号称一次编译,到处运行,就在于它基于一个须要首先先安装到他所谓的到处的JDK,经过JVM解析编译完成后的字节码来运行,跟操做系统的接口也是在JVM托管的。这样的好处是JVM能够在实时运行的时候对字节码进行进一步的优化,也就是大名鼎鼎的JIT,问题是全部的机器上都要安装能够兼容你的应用程序的JDK,同时JVM启动消耗的资源很多,起码数百M,且启动速度缓慢,一样的直接编译成目标操做系统二进制可执行程序的服务,启动起来消耗的资源小不少且速度快了不少。

在当前差别化的芯片结构中,像C、GO、RUST这种能直接运行于操做系统之上不基于某些庞大繁重的VM之上仍是颇有必要的,好比物联网的控制芯片,一般内存也只有几百K,适用性更强一些,并且如今LLVM架构的编译器可以带来性能的大幅优化,因此编译依然是一个很好的选择,除非JIT可以逆天的达到解释执行的极限,所以假如咱们看到某些语言有Java语言的开发能力和内存安全特性,依然是能够考虑的。

5 Haskell, Go, Scala

5.1 Haskell

他虽然很老可是一直是做为学院派函数式语言的表明,其纯函数式的特性和简洁漂亮的语法(糖)让人看了很是舒服,在接触了面向过程和面向对象的开发后,若是要学习一种新的写代码的思路,面向函数式的语言是目前最好的选择了,而Haskell有是函数式语言的先驱和集大成者,不少函数式语言的语法都是从Haskell借鉴来的。

做为纯函数式语言,Haskell将必然会产生Side-Effect的代码好比IO操做放到了一块儿,也即monad风格的部分,而其余的函数能够保证彻底的函数式特征,对于一样的输入不管运行多少次结果都是同样的,跟数学中函数的定义同样严格,函数式是一种CPU友好的语言,在当前多核计算机发展情况下,函数式可让程序很是安全的在多个核心上并发而不用担忧大量的数据交互和side-effect, 从而在语言编译过程当中可以针对并发进行大幅的优化。语言自己的不少写法也跟数学中的定义很接近,好比定义一个集合

ghci> [x*2 | x <- [1..10]]
[2,4,6,8,10,12,14,16,18,20]

看起来很像数学定义,语言可谓优雅漂亮,看着很舒服。做为学院派语言,语言自身设计的要求不可谓不严格,完美的阐述了函数式是什么意思,可是语言的复杂度较高,学习曲线很陡峭,很难保证团队成员的接收程度,也很难招到相关的技术人才。从效率上来说,Haskell能够优化的跟C语言的级别相似,但若是对某些特性不熟悉稍微改动一些就会形成性能的大幅降低,对新手不算友好。

同时在函数式不那么擅长的领域Haskell的商业化程度很低,咱们不可能都用Haskell来写一些语法解释或者正则解析等,涉及IO的分布式存储和计算都相对很初级,尤为是对于咱们比较感兴趣的数据挖掘机器学习领域没有成熟的解决方案,对于Web项目支持的尚可,有优秀的Yesod框架做为表明。

总的来讲,Haskell值的学习但不会在大型的生产环境中使用。

5.2 Scala

  Scala语言的出现目的很明确,感受就是为了替代Java而存在,在Java语言愈来愈力不从心的今天,可以有一门语言既继承了它广大的生态系统,又可以在表达能力和开发效率大大改进的状况,能够说是颇有但愿的。

Scala从一开始就是一门设计良好的语言,几乎完美的集合了函数式的特性和面向对象的特性,虽然他的函数式不是纯函数式。其面向对象的感受更像Ruby而不是Java,全部的东西都是对象,包括简单类型例如Int,以及函数自己都是一种对象,这样在这个层面实现了面向对象和函数式的统一。

Scala运行于JVM之上,可以无缝的使用全部的原来Java语言所开发的各类库,语言上做为Java的超集,迁移过来只会更强大而不会打折。

Java8的出现虽然增长了针对集合的stream api以及Lambda表达式这种函数式特性的支持,但只会让人们以为Java与Scala更像了,即便Java在之后的发展过程当中拥有了全部的Scala的能力.

打个比方一块歪歪扭扭的通过各类后期焊接所建造起来的机器和一个一开始就有目的的设计出来的结构精密、风格统1、表达高效的机器比较,后者更像前者的重构,而前者虽然如日中天但已是暮年的四不像,不停的往身上增长各类各样的功能.

也许Java9会有进步,但如今我看到Java8后反而更倾向于Scala。

Scala的元编程能力可让他修改本身的语言定义,不仅是实现某些业务逻辑,这样从符号层面上,scala能够作到自洽,除了核心的一些规则,其余的均可以被本身根据状态调整所修改,这种能力能够极大的扩展语言自身的能力,固然也带来了一些负面效果,每学习一种新的包不仅是了解他的API,而是学习了一种新的语言,风格可能跟scala大不相同。

强有力的证实,大数据生态系统表明-Spark&Kafka,一个是分布式计算一个是分布式大规模数据吞吐,都证实了Scala的开发能力和效率。

Scala的问题其实也有跟Java相似的地方,首先这个语言虽然是从新设计的,可是使用起来复杂度依然很高,对于范型的继承,+-等范型标注很差理解,

5.3 Go

  Go语言目前呈现了很火爆的趋势,因为其简单,整个语言的specification也不过十几页,最多半天就可以彻底了解并上手写一些小工具。GO语言最初是但愿替代C和C++成为新的系统语言,自带GC垃圾回收,不过最终更多的是替代了python来开发一些服务或者工具,并无成为系统级别的语言。  Go语言有不少的优势,编译速度快,有协程和Channel作并发支持和通讯,有不少官方的网络协议的库,很是适合于写一些网络服务,启动一个http的接口服务只须要几行代码。目前github上也有大量的第三方项目使用go语言来开发应用或者扩展go的功能,在使用的时候直接import便可。Go的多返回机制也还不错,节省了大量的无心义数据结构和不可读的Map的使用,总的来讲Go在其擅长的领域生产力很高,写起来比较流畅,静态类型也足够的安全。目前Docker生态系统里边的各类工具都是Go来写的。最新发布的1.5版本使得交叉编译更加容易,静态连接库的方式使生成的可执行文件在相同CPU架构的操做系统都能运行,减小了额外查找依赖的问题,对咱们如今基本同构的Linux服务器而言,也打到了一次编译到处运行的目的。同时Go语言在运行时消耗的资源也比Java要小,启动速度更快,确实是轻量级服务的优选。  

相关文章
相关标签/搜索