面向对象编程/状态?
如果您不熟悉 状态机,我们不会深入探讨它们,但我们强烈建议您进一步研究它们。这对您思考过程的清晰度有好处。严格地说,状态机是相当复杂的数学结构,并且可以使用数学符号推导出许多有趣的结果。但这不是我们的兴趣。我们的兴趣有两方面
- 一方面,状态机提供了一种清晰的图形方式来说明可能很复杂的应用程序逻辑。首先,您确定可能的狀態。例如,服务器可能处于 IDLE、CONNECTED、BUSY、ERROR 等状态。然后,您定义所有可能的输入,即客户端可能发送的消息,并为每个状态图绘制服务器对每个输入的响应。当您考虑在各种状态下对意外消息的反应时,这有助于提高周密性。
- 其次,状态机是一种使用相对标准符号的标准工具。有一些工具和库可以根据某些规范状态机格式作为输入自动生成代码。这实际上是正则表达式处理器所做的。
在面向对象编程中,“对象”有时是 状态机 的实现。状态机是行为实体的更好概念基础——服务器、正则表达式处理器、图形引擎和虚拟机都是行为实体的良好示例。
简单对象和行为实体之间的基本区别在于,简单对象不会根据其值改变其行为。事实上,我们倾向于将其称为值而不是状态。简单对象往往被保存在集合中并被搜索,传递到函数并从函数返回,并且通常像值一样起作用。字符串和矩形是经典的示例。稍后,我们将讨论消息传递作为方法调用的抽象,在简单对象的上下文中,这可能看起来有点奇怪。
如果您仍然对对象的概念感到困惑,请下载并使用 BlueJ。大多数人在使用该 IDE 几个小时后就会“理解”。
让我们看一下 Ruby 中的示例
我们正在随着时间推移显示某些内容,因此我们同时处理像素和秒。根据缩放级别,它们之间存在相关性:每秒像素数或 PPS。
class Timeline
{
PPS;
current_position_in_seconds;
current_position_in_pixels;
public:
GetPosInSeconds() {current_position_in_seconds;}
SetPosInSeconds(s) {current_position_in_seconds = s;} # oops, we're out of sync
GetPosInPixels() {current_position_in_pixels;}
SetPosInPixels(p) {current_position_in_pixels = p;} # oops, we're out of sync
}
在这个示例中,我们维护了无端的狀態——也就是说,我们同时以秒和像素的形式保存了位置。这很方便,对于此类的用户来说也很重要,但我们在封装方面搞砸了。每当您设置一个单位的值时,您就破坏了另一个单位的正确性。好吧,您说,这里有一个简单的解决方法
class Timeline
{
PPS;
current_position_in_seconds;
current_position_in_pixels;
public:
GetPosInSeconds() {current_position_in_seconds;}
SetPosInSeconds(s)
{
current_position_in_seconds = s;
current_position_in_pixels = s*PPS;
}
GetPosInPixels() {current_position_in_pixels;}
SetPosInPixels(p)
{
current_position_in_pixels = p;
current_position_in_seconds = p/PPS;
}
}
这修复了先前示例中明显的错误,但您为自己创造了很多工作。现在您必须在 Timeline 类中的每个方法中保持所有计算的重复,以保持一致性。可能还有数十个地方需要这样做(相信我)。那么当您添加其他单位(比如英寸)时会发生什么?您必须再次重复所有这些。可以想见,您知道事情会如何发展:计算属性、逻辑属性、虚拟属性,无论您想叫它们什么。让我们再次看一下这个示例
class Timeline
{
PPS; # Pixels Per Second
IPS; # Inches Per Second
current_position_in_seconds;
public:
GetPosInSeconds() {current_position_in_seconds;}
SetPosInSeconds(s) {current_position_in_seconds = s;}
GetPosInPixels() {current_position_in_seconds*PPS;}
SetPosInPixels(p) {current_position_in_seconds = p/PPS; }
GetPosInInches() {current_position_in_seconds*IPS;}
SetPosInInches(i) {current_position_in_seconds = i/IPS; }
}
现在,我们假设 PPS 和 IPS 的转换系数设置正确,为了简单起见,但除此之外,我们极大地简化了我们的问题。在我们接下来写的二十个其他函数中,我们只需要关心秒,没有一致性问题需要担心。此外,如果我们需要将我们的基本单位从秒更改为像素,Timeline 用户永远不需要知道。