使用函数对象来定义运行策略

m88体育 有些语言支持函数指针,代理(delegate),lambda表达式,或者其它类似的机制来存储和传递可执行的"函数"。这些特性允许程序在运行时动态改变行为,而这样的设计被称为"策略"模式(strategy pattern)。比如在c语言标准库中,qsort函数有一个参数是指向comparator函数的函数指针,这个comparator在排序过程中被用来对序列中的两个元素进行比较,通过传递不同的comparator函数,qsort可以动态采用不同的排序(比较)策略,而这个被传入的comparator就可以称为一个排序策略。

Java不支持函数指针,但m88体育通过对象引用可以实现类似的功能。通常说来,调用一个对象的方法,是要对这个对象本身进行操作。但如果一个方法接受的参数是指向其它对象的引用,那么这个方法也可以对这些传入的对象进行操作。如果一个类仅开放一个这样(对其他对象进行操作)的方法,那么它的对象就等同于一个指向该方法的函数指针,而这样的对象被称为函数对象。比如下面的类:

class StringLengthComparator {
	public int compare(String s1, String s2) {
		return s1.length() - s2.length();
	}
}

又在联系一个很不错的北欧职位,难得对方不在乎身份问题,说只要面试通过,他们就可以负责解决签证和工作许可。

于是开始和猎头各种眉来眼去:改简历,试探工资情况。。。看来一切都不错,马上就要正式递出简历,临了猎头这一方还要确认一下技术水平,发个链接过来做在线测试。这是完全正常的要求,而且以前也没少做类似的玩意儿,没啥可怕的,上吧。

结果就杯具了。这家猎头选了一个叫mi-candidate.com的网站,此站简直就是个半成品的破烂。登陆以后找不到测试入口,必须依靠一个莫名其妙的通过邮件发送的链接开始测试。没有任何热身或demo题目让人熟悉测试系统。这些问题也就是用户体验差一些,毕竟不是核心功能,也不用太多吐槽。最坑爹的是他家的题目,连html代码都弄不干净就把题目卖给别人,这就不能忍了。比如一道题目是这样显示的:

继承优于标签类型

开发者有时会遇到这样的类:其对象有多种不同"类别",每一"类别"的对象都用一个特定的标签来识别。比如下面的类,可以代表"圆圈"或"矩形":

// Tagged class - vastly inferior to a class hierarchy!
class Figure {
	enum Shape { RECTANGLE, CIRCLE };
	// Tag field - the shape of this figure
	final Shape shape;
	// These fields are used only if shape is RECTANGLE
	double length;
	double width;
	// This field is used only if shape is CIRCLE
	double radius;
	// Constructor for circle
	Figure(double radius) {
		shape = Shape.CIRCLE;
		this.radius = radius;
	}
	// Constructor for rectangle
	Figure(double length, double width) {
		shape = Shape.RECTANGLE;
		this.length = length;
		this.width = width;
	}
	double area() {
		switch(shape) {
			case RECTANGLE:
				return length * width;
			case CIRCLE:
				return Math.PI * (radius * radius);
			default:
				throw new AssertionError();
		}
	}
}

接口只用来定义类型

如果一个类实现了某个接口,那么这个接口类型可以成为指向该类对象的引用。因此类与接口之间的"实现"关系,应该能使用户通过接口了解对象的行为。除了这个场景以外,定义接口都是不合适的。违背上述原则的一个反例是常量接口,这样的接口不定义任何方法,仅含有静态final属性,每一个属性都被开放为常量。用户类通过实现这些接口来避免使用常量全名。比如下面的例子:

// Constant interface antipattern - do not use!
public interface PhysicalConstants {
	// Avogadro's number (1/mol)
	static final double AVOGADROS_NUMBER = 6.02214199e23;
	// Boltzmann constant (J/K)
	static final double BOLTZMANN_CONSTANT = 1.3806503e-23;
	// Mass of the electron (kg)
	static final double ELECTRON_MASS = 9.10938188e-31;
}

接口优于抽象类

接口和抽象类是Java语言中的两种多态实现机制。两者最明显的区别在于:抽象类允许其本身直接实现某些方法,而接口则必须保证所有方法都是抽象的。一个更重要的区别是抽象类的使用依赖于特定的继承链结构,而接口则没有这种限制,由于Java不允许多继承,抽象类的这一特点严重限制了它们的使用。

已有类可以很容易被改造为新接口的实现,只需声明实现某接口,并添加所需方法即可。比如当Comparable接口被引入时,大量的已有Java标准类被改造为它的实现。对于抽象类,则没有这样的便利。这是因为,想把两个已有类定义为同一个新抽象类的子类,必须把这个抽象类放在两条继承链的交点位置以上,这需要对两条继承链中间部分的所有类做出同样的改造:继承新抽象类,提供抽象方法的实现。很明显,这样做开销巨大,而且很可能破坏继承逻辑。

我这人对使用的所有电脑系统都有洁癖,不能容忍莫名其妙的自动任务。前两天迁移服务器,遇到一个小问题,就属于这类。

装完系统,配置好web服务和awstats统计工具以后,系统经常提示收到内部邮件。用mail命令打开,发现是awstats自动更新时发出的。这个就很诡异,我还没建立cron条目来更新统计信息呢,而且貌似这个默认的更新间隔只有10分钟,对于我网站那一点点微不足道的流量来说,这个间隔无疑是太短了。可是通过crontab命令查询,发现不管是我自己的账户还是root,都没有定义任何cron任务,于是google了一下,找到答案。

原来,在awstats安装时,默认在/etc/cron.d目录下生成了一个名为awstats的文件,其内容是: