1.解耦
程序设计模式的理论多次用到解耦这个概念,例如命令模式将调用者和被调用者解耦,适配器将适配者和被适配者解耦,这和控制理论中的解耦思想很一致,如果输入和输出间的关系复杂,那我们就建立一组合适的状态把他们解耦,现在你只需关注他们和状态空间的关系了。
2.程序变量结构
最近的项目从Redis获取变量,计算后返回Redis。尝试过使用反射批量读取Redis数据到程序变量,发现经常出现Bug,根本原因是程序变量结构复杂。而程序变量之所以使用复杂的嵌套类是为了便于编程。一直纠结于哪种方式好,最后发现其实没有最好的方案,只有不断地平衡。程序语言本身也在开发效率和执行效率之间平衡。对于这个项目来说,既然是为了在编程时明确各个变量的意义而使用嵌套类,就该把那些具有不同物理意义,必须区别的变量用嵌套类组织,而这也意味着这些处理这些变量的工作量客观存在,所以对每个变量分别从Redis读取的工作量也不算什么,不应该过多引入反射。
3.指令式编程和函数式编程
<Programming in Scala>一书中精辟的见解:识别函数是否有副作用的一个重要指示就是函数返回值类型是否为Unit(Void),如果一个函数的返回值为空,那么它起作用的唯一方式就是通过某种副作用。
这句话着实精辟,这也解释了为什么以前的C#控制算法项目中为什么恨不得所有函数的返回值都用Void返回值声明。
4.函数式编程的开销
函数式编程取代循环的时候要使用递归调用,这可能引起函数调用(堆栈操作)的额外开销,但经过编译器优化的尾递归调用将不会为每个递归调用创建新的堆栈结构,所有的调用将在一个结构体内执行。
5.函数字面量
def getFileByEnding(query:String)={
//Pay attention to the use of the closure: 'query' is a free variable while 'f' in method(f) is a bind one queryFile(_.getName.endsWith(query)) } def getFileByContaining(query:String)={ queryFile(_.getName.contains(query)) //for (f <- files if f.getName.contains(query)) //yield f } def queryFile(method: (java.io.File) => Boolean) ={ for(f <- files if method(f)) yield f }分析这段程序,注意体会“传入queryFile的函数 _.getName.contains(query) 被queryFile函数中的局部变量'f'所使用(换成指令式语言的术语:调用)”,在将函数作为头等值的函数式编程中,需要将函数调用变量的思维转过来。这正是“函数式编程语言里,函数和变量一样是头等值”的涵义:考虑程序分为可变部分(参数)和不可变部分(函数),可变部分可以分为两类,即不可变部分调用可变的变量和不可变部分调用可变的函数(后者可更进一步理解为不可变部分(如queryFile)中的不可变变量(如f <- files得到的f))调用可变的函数(如method),所以才说“method被局部变量'f'所使用”。
注意将这个例子与map函数的定义相比较,map函数其实相当于这里的contains函数或者endswith函数,是method“变量”的值,所以他们是函数 File => Boolean。
6.统一访问原则
scala中对无参数函数的推荐定义方法,当方法不改变对象可变状态时不写空括号,即def function:R,这种表示可以支持更高效地将方法实现重构成字段实现,即val function:R。总得来说,字段实现消耗空间,方法实现消耗时间。
7.组合vs继承
ref:Programming in scala chapter10 section11; HeadFirst设计模式 “组合”相关章节;
ps:只有继承才受制于脆基类问题
8.Scala的AnyRef
尽管在Java语言中Object和AndyRef是可互换的,但推荐的scala编程风格是在任何地方都只使用AnyRef,因为AnyDef使得scala可以跨.NET和Java平台。所以说,java.io.object是在Java平台上实现AnyRef的方式。
9.组件的相互调用
《分布式系统:概念与设计》一书中的一句话:大多数中间件在互联网协议上实现,由这些协议屏蔽了实现的细节。回想之前对网络协议的底层和高层的讨论,这里的逻辑倒过来了。实际上仔细想想,跟之前对控制系统的并行性的思考一样,A(s)*B(s)中看起来A(s)先计算,事实上他们在现实中是并行的。很多串行其实都是思考(思维流)形成的,最常见的是因果流被误作为时间流。在这个例子中,中间件调用互联网协议的本质原因其实是:互联网协议较中间件协议先达成标准化。