行为
冯·诺依曼给计算机提出了一个重要的隐喻:一系列依次执行的指令。这里就涉及到了我们需要怎么样来组合这么指令,让它们以什么样的顺序去执行。
控制流(Control Flow)
将运算表达成一系列的步骤。在JAVA中,相邻的语句依次执行。消息被发送去激活一段子程序;异常让控制权从调用栈中跳出。
主体流(Main Flow)
明确表达控制流的主体。就是主要职责,比如保龄球的计分程序,主体流就是计分,其余的都不是。这就跟我们写一篇文章要有一个中心思想一样。
消息(Message)
通过发送消息来表达控制流。消息相当于我们在程序中调用方法或是其他对象或类的方法,从而达到控制程序流程。而其中的参数的不同,带来了不同形态的消息。
选择性消息(Choosing Message)
通过变动一条消息的实现者来表达选项。广泛的使用选择性消息可以减少代码中明确的条件语句的出现。这样更便于后期的扩展,而太多的明确条件语句就需要消耗太多的精力。
public void displayShape(Shape shape,Brush brush){
brush.display(shape);
}
其中我们就可以通过不同的Brush来实现各种不同的画刷。如果写的很明确的话,后期想添加一种画刷类型就需要改动代码,这是很不好的。
双重分发(Double Message)
通过在两条轴线上变动消息的实现者来表达级联的选项。这个类似于选择消息,只不过它是两维的,而选择消息是一维的。同上述的程序,我们可以要求不同的图形用不同的画刷来画。当然我在实际操作中需要考虑两个纬度的变化频率应该是不同的,这里就要考虑那一个纬度为主了。
Kent认为不应该有更多的纬度了,如果出现了需要更多纬度的情况,那么一定是程序设计出现了问题,需要重新考虑自己的设计。
分解性(序列性)消息(Decomposing Message)
将复杂的计算分解成具有内聚性的块。把相关的步骤组合在一起,然后用一条消息去调用它们。就像是启动一辆自动挡汽车(呵呵,只会开自动挡的车)需要插钥匙、踩刹车、点火、挂D档,每次启动汽车时我们都需要调用这些步骤,那么在程序中我们可以把它们放在一个方法中,名字就可以叫做“启动”,这样我们只需要发送“启动”的消息即可完成上述一连串的动作。
当然了,这里消息的名字一定要有意义,让人一看就知道这个消息里会做些什么。
反置性消息(Reversing Message)
通过向同一个接受者发送消息序列,令多个控制流形成对称。Kent认为对称性可以提高代码的可读性,这个也不是Kent在这本书中第一次这样提了。如下代码:
void compute{
input();
helper.process(this);
output();
}
就缺乏对称性,第一个、第三个消息的发送者是this,而第二个缺失helper。这样在阅读时,读者就需要去关注一下helper,因为消息的发起者存在变化。为了保证消息发起者一致,我们可以做如下修改:
void compute{
input();
process(helper);
output();
}
void process(Helper helper){
helper.process(this);
}
这样看起来就好多了,在阅读compute这个方法时就很顺畅了。
邀请性消息(Inviting Message)
通过发送可以用不同方式实现的消息,邀请未来的实现变体。这样未来的变体可能就需要重写该消息或是该消息就是一个抽象的,需要去实现。
解释性消息(Explaining Message)
发送消息去解释一段逻辑的意图。命名很重要,呵呵
异常流(Exception Flow)
尽可能清晰地表达非寻常的控制流,而不干扰对主体流的表达。
卫述句(Guard Clause)
用尽早返回来表达局部的异常流。如果发生条件不匹配就return回去,不再进入后续的流程。我们要优先处理请求的先决条件,而不需要用到复杂的控制结构。如:
void compute(){
Server server = getServer();
if(server == null)
return;
Client client = server.getClient();
if(client == null)
return;
processRequest(client);
}
这就是卫述句的实际用法。其实‘continue’也是卫述句的另一个用法,“不管这个了,继续下一个”
异常(Exception)
用异常来表达非局部的异常流。
已检查异常(Checked Exception)
通过明确声明来保证异常被捕获。
异常传播(Exception Propagation)
传播异常,根据需要转换异常,以便使其包含的信息合乎捕捉者的要求。