编程思想与技术总结

  

      不是天才,但我相信,通过有效的方法和训练,以及持之以恒地积累,完全是可以成为一名出色的开发者的。那么,到目前为止,掌握了多少思想、方法和技术可以用于开发程序、处理软件开发过程中所遇到的问题呢?不妨作下总结,以备后用。

 

      1.   抽象:

      萃取出主要特征,而摒弃次要不相关的特征;无需了解物质的内部实现细节而基于其提供的抽象来构造应用;声明与实现相分离。计算机科学中的抽象俯拾即是,比如汇编语言是对机器硬件的抽象,编译器是对高级程序语言的抽象,进程是对程序一次执行的抽象,线程是对任务执行流的抽象等。

      阅读链接:  《抽象, 编程之道》  

 

      2.  封装:

      由抽象直接引出的重要概念就是封装。函数是算法的良好封装,对象是状态与逻辑的良好封装,  封装是实现软件模块化、提高软件可维护性的重要技术。封装是实现许多软件工程思想的溯源。比如,隔离变化, 将变化的影响局部化等。JAR 包即是 Java 平台实现可复用的库、框架的封装。

 

      3.  复用:

      不要重复发明轮子。复用是软件工程领域的重要技术。从 LinuxShell、标准库函数,STL,JDK这样的代码级复用,到 Struts, hibernate 的应用框架复用, 以及 设计模式的复用, 解决方案的复用, 复用无处不在。 复用是站在巨人的肩膀上, 能够直接利用专家级的知识和经验,何乐而不为?  

      新的更优的解决方案往往是现有可复用方案的组合创新。

      阅读链接:  《编程语言与可复用性》 

 

       4.  分层:      

      将系统分解为多个层次,精确定义每层所提供的服务及层次之间提供的服务接口;处于某个层次的层依赖于下一层提供的服务,并为上一层提供服务;各层之间无需知道彼此的细节。典型例子是网络协议栈及操作系统虚拟机的概念;实际工程项目中的 View - Model - Bussiness - Service - Controller 。《JavaWeb应用开发架构浅谈》阐述了分层思想在 Web 软件架构设计中的实际表现形式。

   阅读链接: 《JavaWeb应用开发架构浅谈

 

   5.  模式:

      在《水平思考的力量》一书中谈到,大脑的运作机制主要是模式机制,即通过模式存储、识别、连接和提取来实现思考。生活中无时不刻使用着模式,模式提高了人们的适应能力和反应能力。设计模式是模式思想在软件设计中的运用,用于构造可扩展性、可维护性良好的软件。JavaIO 是使用装饰器模式实现的优雅而扩展性良好的 IO 框架;《使用规则引擎Drools计算圆周率PI》使用了策略模式去计算 PI, 在实际应用的时候可以通过参数和需要来选择任意一种实现。

  阅读链接: 《javaIO框架小析》

 

   6.  缓存:

      将一部分已求解值存储起来以备后用;或者将一次性存储一部分数据(包括但不仅含有当前所需数据),以便之后紧随着访问。动态规划法是使用缓存思想的典型例子。

      存储器高速缓存是缓存思想的又一典型例子,与程序局部性原理密切相关。要想达到更好的程序性能,则必须采用一定手段,使得访问数据的顺序与数据在存储器中的存储顺序保持一致,这样才能提高命中率,防止大量的缺页中断带来的低效。在实际应用中,分布式缓存、内存缓存也是提升系统查询性能的关键技术手段。

 

 

   7.  中断:

      保存现场, 。。。, 恢复现场。 中断是实现许多计算机制的重要基础。子程序,进程切换, I/0读写……  思想是简单的, 影响是深远的。 

 

   8.  科学原理和性质

      深刻理解科学原理和性质,往往能够催生非常简洁高效的算法和技术,这可能就是计算机科学家与开发者之间的一大区别吧。比如向量的旋转问题,将ab转换为ba, 也是基于对逆置性质的深刻理解上。R(R(a)R(b)) = ba ; 问题: 将 abc 转换为 cba 呢?

      阅读链接:  《向量的旋转问题》 , 《大整数幂求模问题》

 

   9.  模板:

  通过替换静态模板中的动态变量, 从而生成动态文本的思想和技术。WordPress 是模板思想的生动应用,可以定义不同层次的不同优先级的模板,然后将内容应用其上; Velocity 是一款常用的 Java 模板引擎,可以生成服务端页面。《python实现动态更新远程机器列表的SSH登录脚本》展示了模板的最简单应用和实现:定义一个静态模板脚本,里面含有一些字符串变量;然后使用程序生成的内容填充字符串变量,从而生成最终的脚本。《三个 DAL 相关的Java代码小工具》第二个工具,使用模板和正则替换,从 DO 类自动生成 DataTransfer 类源文件。模板通常会与正则表达式匹配和替换联合使用。

 

      10.  多态:

  通过继承与混入的方式,使得子类可以灵活地继承或重写父类的行为; 同一个方法签名可以表现出不同的行为。Scala Trait 语言特性体现了混入与多态的思想。

  阅读链接: 《Scala-Trait:混入与多态

 

      11.  顺序搜索

   顺序搜索是一种简单实用的搜索算法,在大多数时候都是高效可接受的。顺序搜索即是从头到尾或从尾到头逐个检查元素,通过比较找到指定的元素。算法复杂度是 O(n) . 几乎大多数程序都会使用到顺序搜索。如果要进行多次搜索,可以进行预排序,然后进行搜索。

  

      12.   二分搜索技术: 

      用于在有序数组或非连续有序逻辑结构中查找给定关键字, 每次比较可排除一半的值, 时间复杂度为O(logn)。

      例子: 在有序数组里找出给定关键字出现的次数;在不重复的若干整数中找出不存在的整数; 程序调试技巧。  

      阅读链接: 《二叉查找树的Java实现》

 

    13.  排序

  排序无疑是计算领域中非常重要而又实用的技术,也是必知必会的技能之一。选择排序、插入排序、冒泡排序、归并排序、快速排序、堆排序、二叉排序、桶排序、计数排序、多关键字排序、位图排序,任君选择。在实际应用中,往往要根据对象的某个属性进行排序, 需要编写比较器函数,或是实现比较器类, 比如 Java 的 Comparator, Comparable ,  C quicksort 里的比较函数。通常会选择快速排序、归并排序、堆排序。

 

  14.  分区技术

      给定一个列表,从中选定一个主轴元素,将它置于某个位置,并将列表中的元素分为两个部分:使前半部分的所有元素都不大于该元素,后半部分的元素都不小于该元素。应用:快速排序,找出第k大的元素(顺序统计量),请参阅《算法导论》第7章(快速排序)和第9章(顺序统计量)。

 

  15.  分治技术

      将一个任务分解为多个子任务,分别求出这些子任务的解,最后综合这些子任务的解,得到总任务的解。时间复杂度是T(n) = aT(n/b) + G(n),主要因素取决于a, b , G(n)。 例子: 最近对问题,归并排序。分治是一种极为重要的基础编程思想,在软件工程领域也有广泛应用。

      阅读链接:  《一个字符串问题的思考》

 

  16.  动态规划法 

  动态规划法是在分治思想上,为避免重复子问题求解而产生的一种算法技术。通过在计算过程中缓存和复用已求解子问题的解,从而为更大规模的子问题计算节省时间。如果分治技术的最终解通常是由子问题的解组合而成,那么动态规划法的最终解是通过子问题的解逐步递推而出。《二项式定理(Java实现及代码重审)》 使用了动态规划法缓存子问题的解的思想来更高效地求解二项式的系数值; 《0/1背包问题的动态规划法求解 —— Java 实现》是动态规划法的典型应用; 单源最短路径算法也是动态规划法的优秀示例。一般具备序列累加性的问题特征可考虑动态规划法求解,比如最长公共子序列、数组最大和问题。

   

  17.  递归技术:

      一个规模为N的问题的解可以由规模为S(S<=N)的同样性质的问题的解来构造。T(N) = T(N-n) + G(n) 或 T(n) = mT(N/m) + G(s). 递归技术是一种非常有效的程序设计技术。计算机中的绝大多数数据结构都有递归特性。列表、字符串、二叉树、JSON 等。

      阅读链接: 《二叉树的JAVA实现》 ,   《减一技术应用:生成排列与幂集(Java实现)》 , 

                     《二叉树漫游——编程技术与技巧总结(上):递归技术》, 

                     《二叉树漫游——编程技术与技巧总结(下):递归转换为非递归》   

        《一个Json结构对比的Python小工具兼谈编程求解问题》     

            

      18.   时空权衡技术:

      通过额外操作以时间换空间,或者额外空间存储以空间换时间。在PC机上,通常是以空间换时间,例如,哈希表就是一个典型例子。

      以时间换空间的例子,比如说在内存不足的情形下进行海量数据处理,可以采用k 趟遍历,每趟遍历解决一部分数据,得到一些子结果,最后将子结果合并。这样在I/O读写上可能耗费较大量的时间。当k比较小时,采取k趟排序是一个较好的选择。

   

      19.  数组结构: 高效随机访问的下标集到值集的映射。

       a)   下标集是存储连续的,即最简单的数组形式;

       b)   下标集是非连续的, 例如存储稀疏矩阵的元组集合

       阅读链接: 《使用 highchart 绘制柱状图的通用方法与接口》

 

      20.   链表:

      a)    单链表: 在列表中高效地插入、删除;

      b)    多重栈、多重队列实现;

      高效查找的哈希表使用了链表; AbstractSynchronizedQueue 也使用了链表来管理其等待队列。

 

      21.   栈: 先进后出型数据结构,仅允许在一端进行插入和删除。

       a)    深度优先搜索的辅助数据结构,比如回溯技术

       b)    典型应用: 递归函数,表达式计算,走迷宫

       阅读链接:《栈与回溯:迷宫问题

 

    22.   队列: 先进先出型数据结构,仅允许在一端进行插入,在另一端进行删除。

      a)    FIFO服务的最佳选择,比如操作系统中各种就绪、阻塞队列

      b)    广度优先搜索的辅助数据结构,比如二叉树的层序遍历

      “生产者-消费者” 并发模型中使用队列作为生产者和消费者的通信和同步的共享存储空间,阅读链接: 《生产者-消费者问题》

 

       23.   二叉查找树: 

       在平均情形下,查找、插入、删除、最大元素、最小元素、前驱元素、后继元素的操作均为O(logn),最差情形是O(n)。中序遍历可用于排序。

       阅读链接:  《二叉查找树的JAVA实现》

 

       24.   散列表:  

      通常主要支持查找、插入和删除操作,散列表是实现高效检索的重要数据结构; 各种编程语言、框架都提供了 Map 容器实现,使用了散列表实现; 散列表是必须要掌握的最重要最常用的数据结构之一。单词计数使用了 Map 来存放每个单词出现的次数; 前缀匹配使用了 Map 来存放对于每个字符串的前缀匹配的字符串集合。

      在平均情形下,这些操作可以在O(1)内完成;在最差情形下,在O(n)内完成。 

      阅读链接: 《自己写的一个哈希表的实现》,  《输入自动提示与补全功能的设计与实现

 

     25. 位图技术:

       将给定元素标识成一个位,从而在后续处理中标识该元素的存在。可用于稠密集合的排序,节省空间资源。

       阅读链接:  《位图排序(位图技术应用)》

 

       26.  堆结构:

       在大量数据中用来查找前K大的元素,实现优先级队列以及排序。请查阅算法导论第八章(堆排序)

       阅读链接:  《使用堆查找前K个最大值》

 

       27.  表结构:

      表结构是计算机领域的常用结构,比如编译器符号表, 中断处理程序表,  数据库表等。 表结构也可以看作是 “空间换时间” 的例子, 通过表项事先存储一定信息,然后通过下标就可以迅速找到相应的程序入口地址或者数据项。表结构通常与索引技术结合使用。

 

       28.  算法分析: Ο(n), Ω(n), Θ(n)

       通过统计基本操作的次数,并取主项,可以得到算法运行的时间复杂度; 可以有效预测程序的性能级别, 及时排除不可接受的算法。

        a)   Ο(n): 算法的上界,即至多要运行的时间近似;

        b)   Ω(n): 算法的下界,即至少要运行的时间近似;

        c)   Θ(n): 算法的精确界,不大于O(n),不小于Ω(n)

        NOTE: 这里通俗地说明三种界,精确定义请参考相关的算法书籍。

 

       通过取阶、无穷小可以快速判断算法复杂度。比如

        3n^2 + 2nlogn = O(n^2) 这是因为:

        (3n^2 +2nlogn)/(n^2) 对 n 取极限(当n趋于无穷大时)为零,而

        (3n^2 +2nlogn)/(nlogn) 取极限为无穷大,因此,

        3n^2 + 2nlogn = O(n^2), 3n^2 + 2nlogn =Ω(nlogn)

 

       通过对程序中的嵌套循环层数可以快速判断算法的时间复杂度;但有些情况是例外,比如外层循环的下标根据内层循环的下标进行跳跃性前进。   

       阅读链接:  《常用算法设计技术总结》

 

       29.   算法的三种情况分析:

        a)   最优情况分析: 即找出一组输入,使该算法在最短的时间内完成。比如插入排序的最优情况是输入基本有序,时间复杂度为O(n)

        b)   最差情况分析: 即找出一组输入,使该算法在最长的时间内完成。比如快速排序的最差情况是输入基本有序,时间复杂度为O(n^2)

        c)   平均性能分析: 通过比较精确的数学分析方法,求出该算法在一般输入下的算法运行时间。比如快速排序的平均性能在O(nlogn), 插入排序的平均性能在O(n^2)

 

       30.   索引:

       “以空间换时间”的应用。通过标识常用项,从而在查询时能够快速定位提升效率的方法; 索引的实现通常采用 B 树或 B+ 树。数据库索引是索引技术的最重要实现之一。

 

       31.  记法与解释器:

      通过记法来表达问题空间,然后,编写解释器来解析记法,得出结果。广义来说, HTML, CSS, XML , JSON, Word, Excel, Markdown, Wiki ,  编程语言, 都是创建了一种有表现力的标记系统,能够将内容以一定的格式进行表现,也因此可以通过程序相互转换。

      例子: 图案打印。先创建一套记法或指令,描述图案的画法,然后编写解析器来解析这些记法或指令,从而打印出图案。

      阅读链接: 《图案打印: 记法与解释器》

 

       32.  正则表达式

      正则表达式是用来描述字符串的模式语言,比如 \\d{4} 可以描述4个数字组成的字符串; ^[0-9]*[1-9][0-9]*$ 可以描述任意正整数。 正则表达式是文本处理的强有力工具。《正则表达式基础知识》概括了正则表达式的基础知识,Scala正则与抽取器:解析方法签名》 给出了正则的一个实际应用:解析方法签名, 从 DAO 中自动生成 Service.  

 

      33.   异或妙用:

       0^0 = 0,0^1 = 1, 1^0=1, 1^1 = 0. 异或满足交换律和结合律,即a^b^c = a^c^b = b^a^c = b^c^a = c^a^b = c^b^a ;对于整数a,b来说,若a = b,则 a^b =0 ; 若 a^b = 0, 则 a = b ; a^0 = a, a^a = 0, 因此 b^a^b = a 异或可以消去偶数次重复出现的数。        

       ①  不用临时存储空间交换两个整数。a = a^b, b = b^a, a =a^b, 实际上就是 a = a^b, b = b^(a^b) = b^b^a = a , a =(a^b)^a = b

       ②  有2k+1 个数,其中 k 个数出现了2次,一个数出现了1次,请找出这个只出现了一次的数。

 

      34.   “哨兵元素”:

      在连续存储块(比如数组)的边界设置一个哨兵元素,从而减少比较次数,或者防止越界。

 

      35.    断言与不变式:

      使用断言来检测函数参数的有效性,验证前置条件是否成立;在程序的适当位置插入断言,验证程序执行状态是否正常;在程序的结尾插入断言,验证程序的后置条件是否成立。 断言通常用于单元测试中, 不变式可以用来证明程序的正确性。通过设置合理的不变式,并证明在顺序、选择和循环中不变式总成立,从而证明程序的正确性。

 

      36.   自动化

      自动化使得人从繁琐易错的事情中解脱出来, 从事更具有创造性的工作,  使人与计算机并行协作, 在计算机干活的时候人休息一会也不耽搁事情。善于发现可自动化的流程、规律、事务, 尽可能使用程序自动化和简化手工活, 减少或消除繁琐易错的手工操作。

      阅读链接:《Debian6.0.5 squeeze 一站式开发环境安装脚本》,《使用getopt解析命令行一例》 《pkgm : 动态压缩包维护与安装脚本》

  

  37.  标记变量实现可选项

  在业务流程中, 总有主要流程和可选流程。可以使用标记变量实现可选流程。比如 $flag = true 执行某段逻辑,否则不执行。

 

  38.  高阶函数与 lambda 表达式

  可以将函数传入函数,从而使得函数更有表达力,就好像把蜘蛛侠或蝙蝠侠的能力传入了超人,而超人就可以使用蜘蛛侠或蝙蝠侠的能力;如此叠加可以获取极为强大的表达能力; lambda 是短小函数的简记法。

  阅读链接: 《PythonPP+lambda:示例

 

      39.  闭包

  一种可以创建“在一次花开中诞生,在花谢中不随之而亡, 而在另一次花开中依然能够存在的富有生命力的对象” 的技术。在 《一个图片文件批量重命名工具的质量改善过程》中使用了闭包来创建可复用的生成图片编号的函数。

 

      40.  匿名函数

      匿名函数通常是临时需要使用而定义的短小函数, lambda 表达式、闭包都是匿名函数的形式。匿名函数可以实现特殊的用途。

    阅读链接:《使用匿名函数在回调函数中正确访问JS循环变量》 

 

   41.  函数柯里化(Currying)

      ”函数柯里化”是指将多变量函数拆解为单变量的多个函数的调用, 可以从高元函数动态地生成大量低元的函数。可以看成一个强大的函数工厂,结合函数式编程,可以叠加出很BT的能力。

       阅读链接:《函数柯里化(Currying)示例》 

   

   42.  JSON 数据格式

   JSON 是一种轻量级的简洁的跨平台的数据交换格式, 在 Restful Web系统的服务端-客户端通信中广泛使用,在 Javascript 是首选数据结构。 JSON 是一种递归结构,需要使用递归技术来解析。

  阅读链接:《一个Json结构对比的Python小工具兼谈编程求解问题》, 《javascript编程: JSON, Mapping, 回调

 

       43.  XML 数据格式

   谈到 JSON 就不能不说 XML .  JSON 作为轻量级数据交换格式,常用于服务端与客户端交换数据, 而 XML 作为较重量级数据交换格式,通常用于应用的配置文件、协议标准数据格式、自定义标记语言等。XML 实际上用途比 JSON 更广泛一些, 但开发通常接触 JSON 会更多一些。

 

   44.  胶水语言

   胶水语言是指那种能将现成的工具组合起来使用从而展示更强大威力的语言,比如 Shell , Groovy . 

   阅读链接:《Linux 命令和工具学习笔记

 

   45.  工具与快捷方式

  工欲善其事,必先利其器。建立适合自己的快捷键是提升效率的有力途径之一。Linux 命令 ln 可以用于建立文件或程序的快捷方式。

  阅读链接: 《构建自己的快捷键设置》, 《VIM 编辑器笔记》 

 

   46.  MD5

      MD5 是信息摘要算法,用于将任意长度的字符串转化为唯一的不可逆的固定的 128 位的字符串。MD5 常用于检测文件传输的完整性;在实际项目中,可以对请求参数做 MD5,以识别是否是完全相同的请求并加以限制或重用。 

 

      47.  DES

      DES 是数据加密解密算法,在实际项目中,通常用于 API 请求签名。

 

      48.  管道、过滤器与链式处理

   管道与链式处理是过程式编程思想的核心。通过管道将现成的小程序小工具组合成处理链,通过过滤器决定是否需要传递给后续处理,将数据通过这个处理链而得到最终结果。Shell 管道与 Web 中的过滤器链、拦截器链都是很好的应用。

 

    49.  异步与回调

   当在请求中要进行一个耗时很长的任务时,往往不能一直等待着它的完成。通过使用异步与回调, 在调用其任务之后立即返回,而在任务完成后进行回调处理,可以有效地提升单次请求的响应速度和体验。典型应用是客户端与服务端通信的 Ajax 技术, 当然在服务端后台的分布式结构中也可以采用异步回调技术。《使用 jsPlumb 绘制拓扑图 —— 异步加载与绘制的实现》展示了如何使用 Ajax 异步地绘制完整的拓扑图的实现。《编程模式: 回调》简明地讲述了回调的含义及实际用途;混合使用 ForkJoin, Akka, Future 实现一千万个不重复整数的排序》 使用全异步方式实现大文件的归并排序。

 

  50.  消息订阅与通知

  常常与异步编程结合使用。当客户端要进行耗时很长的任务时, 可以订阅一个相应的消息,然后做其它的事情;当服务端完成任务后,将推送消息给客户端,然后客户端接收消息后进行后续处理。需要可靠性高的消息中间件、等待队列来完成。

 

    51.  资源池

   常常要创建一些对象做一些特别的工作,比如线程用于执行任务,数据库连接来与数据库通信执行SQL,而创建这些对象往往是开销很大。如果不加限制地创建,在实际应用中并发数很高或峰值状态下很容易就会导致应用崩溃。因此,采用资源池的做法,在池中预先创建一部分这样的对象,在需要时增加并限定最大数,在空闲时释放以节省资源,并确定何时增加和释放,有效地管理这些对象。典型应用有线程池、数据库连接池。

     阅读链接:《多线程并发: 带任务描述和执行时间的线程池执行简易框架》 

 

    52.  日志

  日志是应用的重要组成部分,用于记录系统运行过程中的状态、错误信息等,便于后续排查问题及统计分析。Java日志终极指南》给出了日志系统的基本概念、设计思路、实际使用相关的许多知识。

 

  53.  代码质量

  能够编写出品质过硬的可信赖的程序也是必须掌握的技能之一。《如何编写可信赖的代码》给出了编写可信赖程序的若干实用建议; 《Web服务端软件的服务品质概要》给出了评估服务端软件的各种质量属性;一个图片文件批量重命名工具的质量改善过程》通过一个示例阐述如何逐步地提升程序的质量。

 

      54.  并发之线程同步模型

     并发是为了尽可能利用计算设备的资源、更高效快速地完成既定任务的技术手段。 通常, 大数据集可以使用多台机器或多个线程或多个进程并发处理而后汇总; 一个复杂任务也可以分解为多个流水线子任务的相互并发协作来完成。并发存在一定的出错风险、死锁风险。

     阅读链接: 《python实现指定目录下JAVA文件单词计数的多进程版本》

 

  55.  并发之 AKKA 模型

  使用 AKKA 模型, 即创建一系列完成不同任务类型的角色 Actors , 在 Actors 之间通过消息来通信, 避免了不恰当同步导致的并发问题。"通过消息传递来共享内存,而不是通过共享内存来传递消息",正是 AKKA 模型与线程同步模型采用的思路差异之处。

  阅读链接: 《使用Scala实现Java项目的单词计数:串行及Actor版本

 

     56.  ForkJoin 框架

   ForkJoin 是分治思想的框架性实现, 将原问题分解为同样性质的多个子问题,然后将子问题的解组合起来得到原问题的解。通常采用二分法。实现上,通常会采用递归结构。《混合使用 ForkJoin, Akka, Future 实现一千万个不重复整数的排序》 使用 ForkJoin 框架来生成一千万个不重复的整数。

 

      57.  规则引擎

  使用领域语言将业务规则表达成一系列的规则集合,业务流程表达为规则流,使用规则引擎来解析规则流和事实对象的数据,从而获得最终结果的技术。规则引擎是一种更加智能、动态可配置的业务领域技术。Rete 算法是规则引擎实现的知名高效算法之一; Awk 语言实现了一种基于模式匹配的小型规则引擎; Drools 是 Java 平台的一款成熟的规则引擎产品。

  阅读链接:《基于规则和规则引擎的系统》, 《Java Drools5.1 规则流基础【示例】(上)

 

  58.  总结归纳

  学到的东西就记录下来, 事无巨细, 积微至著。 不定期地总结、归纳和回顾已经学到的知识和技能, 对于职业发展是非常有益的。

  阅读链接: 《软件开发之技能梳理》

 

 

posted @ 2011-10-16 10:36  琴水玉  阅读(601)  评论(0编辑  收藏  举报