编程语言与可复用性
编写更少BUG 程序的一个技巧是: 切分大型逻辑为能够容易处理的小逻辑块; 尽可能复用经过严格测试的可靠成熟的公共库。
当实现可复用的目标时, 通常是希望尽可能少的代码能够表达更强的复用性, 而不要受到与问题无关因素的制约。 语言的设计会对可复用性的实现有较大影响。静态类型语言通常会施加更多与问题域无关的类型限制,以增强运行时的可靠性; 而动态语言则通过类型推导机制,使得编程者尽可能少地受到类型的约束,而在运行时的可靠性由程序员自己去保证。
例子: 取出任意对象列表中的指定字段的值集合。
使用 Java 实现是这样的:
package com.qinshuq.zk.study; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; public class GettingFieldValues { public static void main(String[] args) { printAllNamesAndAges(); printAllNamesAndAges2(); } public static void printAllNamesAndAges() { // Get all ages of persons List<Person> persons = createPersons(); List<Integer> ages = new ArrayList<Integer>(); for (Person p: persons) { ages.add(p.getAge()); } System.out.println(ages); // Get all name of cats List<Cat> cats = createCats(); List<String> names = new ArrayList<String>(); for (Cat c: cats) { names.add(c.getName()); } System.out.println(names); } public static void printAllNamesAndAges2() { List<Person> persons = createPersons(); List<Cat> cats = createCats(); System.out.println(getAllFieldValues(persons, Person.class, "age")); System.out.println(getAllFieldValues(cats, Cat.class, "name")); } private static <T> List<Object> getAllFieldValues(List<T> objList, Class c, String objFieldName) { List<Object> values = new ArrayList<Object>(); try { for (Object obj: objList) { Field f = null; f = c.getDeclaredField(objFieldName); f.setAccessible(true); values.add(f.get(obj)); f.setAccessible(false); } return values; } catch (SecurityException e) { return new ArrayList<Object>(); } catch (NoSuchFieldException e) { return new ArrayList<Object>(); } catch (IllegalArgumentException e) { return new ArrayList<Object>(); } catch (IllegalAccessException e) { return new ArrayList<Object>(); } } public static List<Person> createPersons() { List<Person> persons = new ArrayList<Person>(); persons.add(new Person("qin", 30)); persons.add(new Person("shu",29)); return persons; } public static List<Cat> createCats() { List<Cat> cats = new ArrayList<Cat>(); cats.add(new Cat("mimi", 2)); cats.add(new Cat("nini", 1)); return cats; } } class Person { private String name; private int age; public Person(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } } class Cat { private String name; private int years; public Cat(String name, int years) { this.name = name; this.years = years; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getYears() { return years; } public void setYears(int years) { this.years = years; } }
由于 Java 对类的元信息【成员、方法、包等】进行了良好的抽象,因此可以编写一个通用的方法来实现对象列表指定字段的值集合。程序中, 使用了反射机制来获取对象字段的值, 同时, 需要与 Java 类型系统打交道,必要时进行强制类型转换,在实现的时候必须考虑与问题域无关的语言类型系统的约束和类型安全。
再看看 Javascript 的实现。 Javascript 是一门“无类”的基于原型的面向对象语言。使用 Javascript 时,就不必要编写类的定义了,直接针对对象操作,而对象可以使用 JSON 格式来简洁地表达,针对对象取属性值也有简洁的支持; 美中不足的是, Javascript 没有提供对列表的 map 映射,需要自己去实现。代码如下:
cats = [{"name":"mimi", "age":2}, {"name":"nini", "age":1}];
persons = [{"name":"qin", "age":30}, {"name":"shu", "age":29}];
Array.prototype.map = function(handleElem) {
if (this == null || this.length == 0) {
return [];
}
values = [];
for (var i=0; i< this.length; i++) {
values.push(handleElem(this[i]));
}
return values;
}
Array.prototype.getFieldValues = function(fieldName) {
return this.map(function(obj) { return obj[fieldName]} );
}
console.log("using prototype to extend array's ability");
console.log(cats.getFieldValues('name'));
console.log(cats.getFieldValues('age'));
console.log(persons.getFieldValues('name'));
console.log(persons.getFieldValues('age'));
实际上, 在这个需求中, 只需要关心三个概念: 列表、 对象字段、 遍历值。 即: 遍历列表, 取出对象的指定字段值。 不需要去关心是什么对象, 值是什么类型。 使用 Python 的实现如下:
#!/usr/bin/env python # -*- coding: utf-8 -*- # ------------------------------------------------------------------------------- # Name: GetSpecFieldValues.py # Purpose: extract all values from specified field of object in a list. # # Author: qin.shuq # # Created: 2015-03-12 #------------------------------------------------------------------------------- def getSpecFieldValues(objlist, fieldName): return map(lambda obj: obj.__dict__[fieldName], objlist) class Person(object): def __init__(self, name, age): self.name = name self.age = age class Cat(object): def __init__(self, name, years): self.name = name self.years = years if __name__ == '__main__': persons = [Person('qin', 30), Person('shu', 29)] cats = [Cat('mimi', 2), Cat('nini', 1)] print getSpecFieldValues(persons, 'age') print getSpecFieldValues(cats, 'name')
其中 getSpecFieldValues(objlist, fieldName) 只需要一行代码, 就能适应各种对象的各种字段的值类型 !
对比分析
作为一门中规中矩的静态类型通用网络语言, Java 能实现相同的目标, 只是拐弯抹角要绕一些圈儿, 写起来有点费力, 代码也不够优雅, 难以自由表达我们所思定的概念及交互, 不过作为大型服务端应用系统的工程语言 Java 做得是很不错的; Javascript 作为面向终端的浏览器端语言,则显得更加“时尚”一点, JSON, 基于原型的对象思想,略带函数式的编程, 使得其在表达力上有较大的灵活性,而在工程性上略逊一筹; Python 作为简洁的动态语言以及拥有成熟的面向对象特性, 表达力更加灵活, 工程性也很不错。
语义理解
当我们在复用他人的工作成果时, 需要准确理解开发领域内各种概念、现有技术的逻辑语义。 编程语言提供的语言特性, 比如 Synchronized 的语义; API 语义,比如 map(lambda, list) ; 技术点的适用语义 , 比如数据库读写事务 ; 应用框架中自定义实现的语义,比如 Spring 里的拦截器, AOP; 开发工具中的模型语义, 比如 POM 的生命周期管理。程序员所做的事情, 就是准确理解和应用这些语义来搭建与现实世界衔接良好的趋于精确完美的逻辑系统。软件开发,从技术角度来说,是语言与逻辑的艺术。
编程的本质是简明自然地表达和维护大型逻辑。