两个Account的子类

本章接着上一章演示如何创建类和创建子类。在本章你将要创建两个特殊的Account的子类:Checking和Savings。我们将继承Account的能力,同时特意使用两个对象,以便更好地管理账号特定的内容。

Savings类: 两个类中的一个,我们将把它放一起

我们创建Saving类作为Account的子类。用来像Account那样存钱,但是新增一个属性,用来依据结余支付利息。我们Saving作为Account的子类:

        Account subclass: Savings [
            | interest |

这已经告诉我们:实例化变量将会计算利息支付。因此除继承Account得到的spend:和deposit:消息外,我们将需要定义一个方法去添加利息存款,然后每年在付过税后清除利息变量。我们先来定义一个方法分配一个性的账户。我们确保利息的范围从0开始。
我们可以在Account的子类里作这一切:存储范围
We can do so within the Account subclass: Savings scope, which we have not closed above.

        init [
            <category: 'initialization'>
            interest := 0.
            ^super init
        ]

再次调用时父要处理new消息,创建一个大小适当的对象。在创建后,父仍旧发送init消息给新对象。作为Account的子类,新的对象先收到init消息;设置我们的实例变量,之后init消息被向上传递到链,其父处理他的初始化部分。
随着我们的新账户Saving被创建,我们可以定义两个方法来处理这个帐号的特殊操作

        interest: amount [
            interest := interest + amount.
            self deposit: amount
        ]
        clearInterest [
            | oldinterest |
            oldinterest := interest.
            interest := 0.
            ^oldinterest
        ]

现在定义完了,现在闭合整个类

     ]

第一个方法告诉我们添加我们添加amount来计算总利息。self deposit: amount这一行让Smalltalk给对象自己发送了一个消息 ,在这里是deposit: amount。这之后英文Smalltalk查找deposit:模块,实际在父中寻找到后运行,修改我们的总结语balance变量。
你也许会纳闷为什么我们不简单的 balance := balance + amount。答案依赖于面对象语言尤其是Smalltalk的一个原则。我们的目标是仅仅编码一个技术一次,然后再需要的时候重用。如果我们直接在这里使用balance := balance + amount。当需要修改balance表示存款的时候就有两个地方需要改。这里或许看起来没什么实用的区别。但是考虑到假如以后我们决定开始继续这种记录存款总数的方法。假如我们都写成balance := balance + amount 在每一个地方。当需要修改balance时,我们不得不搜寻每一个用到的地方来修改。如果发送自生deposit:消息,我们仅需修改这个方法一次,所有的接收者都会自动获取到最新的正确的方式来更新balance。

第二个方法chearInterest很简单。我们创建了一个缓存变量oldinterest来保存当前利息总数。我们在新年开始之后归零,用返回的之前的利息作为结果。所以我们在年末会计可以看到我们产生了多少利息

Checking类: 这里还有其他的东西
我们第二个Account的子类代表了支票账号。我们将跟踪两个面:
我们的支票的总数
开处的支票总数
我们将定义另一个Account的子类:

     Account subclass: Checking [
         | checknum checksleft |

我们有两个实例变量,但我们仅需要初始其中一个,如果没有开处支票,现在的字票数就不是问题。记住,我们的父类给我门发送init消息,我们不需要我们的类使用特殊的new函数,我们的父将会作有我们需要的工作。

         init [
            <category: 'initialization'>
            checksleft := 0.
            ^super init
        ]

如同在Savings中,我们继承了大量功能从我们的父类(superclass)Account。在初始化中我们仅仅设置支票总数为0,而没有设置checknum。我等会在父类中进行初始化。

写入支票

我们将在添加通过支票花钱的方法后结束本章。发送消息,更新变量的机关类似这样:

        newChecks: number count: checkcount [
            <category: 'spending'>
            checknum := number.
            checksleft := checkcount
        ]
 
        writeCheck: amount [
            <category: 'spending'>
            | num |
            num := checknum.
            checknum := checknum + 1.
            checksleft := checksleft - 1.
            self spend: amount.
            ^ num
        ]
     ]

newChecks: 用支票填充我们的支票本。我们记录我们最初的的支票数,更新支票簿里的支票总数。

writeCheck:仅仅下一个支票数,之后提升支票数,记录支票总额。消息 self spend: amount给对象自生发送spend:消息。导致这个方法被Smalltalk查找。最终在父类Account中找到,我们的总额被更新反应出我们的消费。

可以试试下面这些例子:

        c := Checking new
        c deposit: 250
        c newChecks: 100 count: 50
        c writeCheck: 32
        c

消遣以下,或许你想为Checking类添加printOn:消息,这样就可以看到Checking的特殊信息了。

在这一章,你已经知道如何为自己的类添加子类了。你也他先了新的方法,从父类继承方法。这些技术提供了结构化解决问题的主要方式。在下一章我们将深入语言机制和类型的细节,同时提供如何在Smalltalk中调试的细节。