Making a BankAccount (and an ATM to get the money)
- A Tweak tutorial -

Table of Contents:
  1. About this tutorial
  2. Making the BankAccount
    1. Creating the 'balance' field
    2. Initializing the Account
    3. Depositing and withdrawing money
    4. Testing the account
    5. Unifying balance and value
  3. Making the ATM
    1. Defining the UI elements
    2. Setting up the costume
    3. Displaying the balance
    4. Watching changes in the account
    5. Making the buttons work
  4. Summary

About this tutorial:

This tutorial is an introduction to Tweak. The tutorial itself had been originally written for Squeak but it turned out to be a wonderful way to (at least briefly) show many of the important aspects in Tweak.

It should be understood that this tutorial is not an introduction to programming -- indeed the reader is expected to have some programming knowledge in Squeak. For reference (and the "real" tutorial which might be helpful if you are a total newbie to programming) see http://www.squeak.org/tutorials/BankAccount.html.

Back to top


Making the BankAccount class

The first thing we need to do is to define us a BankAccount class with the classic deposit and withdraw operation. In Tweak you use a so-called "Class Browser" which is (at the time of this writing) available under the Tools menu. When you open the browser you need to create a new category (call it "Tutorial").

In the class browser fill in the class definition template as shown here:

CObject subclass: #BankAccount
	instanceVariableNames: ''
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Tutorial'

Notice that the above says "C"Object and not just "Object" - the "C" prefix is widely used throughout Tweak since Squeak still doesn't have name spaces. Accept the class definition and BankAccount should now show up in the browser's class list (if it doesn't, you likely pressed Alt-S instead of Ctrl-S on Windows).

Back to top


Creating the 'balance' field

For our bank account to work we need something hold its balance so let's define it. Change the class definition to read:

CObject subclass: #BankAccount
	instanceVariableNames: 'balance'
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Tutorial'

And accept it (Ctrl-s/CMD-s). You will notice two things that might be atypical to a Squeak user: First of all, the name "balance" is printed bold, and second, accessor methods (#balance and #balance:) have been automatically generated.

ADVANCED: What is going on here? Well, in reality you haven't defined an "instance variable" in Squeak but rather what is called a "field" in Tweak. Tweak treats the class template as a user interface for the class definition (rather than an expression) and "adding a name" (such as balance) is interpreted as a request to define a field. (if this sounds confusing, don't worry - for all practical purposes you have just added a variable, that's all).

By default, these are (what we call) "regular fields" (it's a bad name, yeah, I know...) but you can change what kind of field you'd like to have by right clicking on the balance field and selecting the type of field from the context menu.

The menu lets you choose between the following types of fields:

WARNING: Adding a field, or changing the field type will destroy any existing code named like the generated accessors.

Back to top


Initializing the Account

In order to initialize our BankAccount properly, let's add an initialization method. First, create a method category (call it "initialize") and enter the following code:

initialize
	"Initialize the account"
	super initialize.
	balance := 0.

You can assign to all fields regardless if they are regular, virtual fields, or instance variables. In addition you might see (what I tend to call) "remote assignments" in some places that is assignments of the form "foo bar := 42". For example, the above could have been written as:

initialize
	"Initialize the account"
	super initialize.
	self balance := 0.

Also note that contrary to what you might be used to in Squeak, all regular and virtual field accesses are represented as messages (this is why the accessors are automatically generated) which can be seen by looking at the "decompiled" version of the methods.

Back to top


Depositing and withdrawing money

The next step in our tutorial will add some operations to the bank account. This is straightforward (nothing Tweak specific here) so we will merely add a new method category (call it "operations") and enter and accept the following methods:

deposit: amount
	"Deposit new funds in this account"
	balance := balance + amount.

withdraw: amount
	"Withdraw funds from this account.
	Inform the user if not enough funds are available."
	amount > balance
		ifTrue:[^self inform: 'sorry, not enough funds'].
	balance := balance - amount.

Back to top


Testing the account

Now that our bank account is ready to use let's see how it works. Open a workspace and execute the following code:

	BankAccount new inspect.

You will see an inspector like the following:

Notice that besides the "balance" field there two other fields already - "name" and "value". These two are fundamental for all Tweak objects since all objects can be named and all objects can represent other objects. The latter is what the value field means -- if an object is a representative for another object (for example: a check box representing a boolean value; a text editor representing a string or text, a numeric field representing a number etc) then its value will contain the object represented.

Back to top


Unifying balance and value

Given what we just said about representing other objects, it seems that, really, our bank account is just an interface to its balance so perhaps we should unify balance and value. One way in which we can do this is by using virtual fields:

From the context menu of the "balance" variable, change it to be a virtual field. Then, add the implementations for #balance and #balance: as follows:

balance: aValue
	"Modify the receiver's balance"
	^value := aValue

balance
	"Answer the balance of the receiver"
	<bewareOf: #valueChanged>
	^value

At this point, ignore the <bewareOf: #valueChanged> annotation for the time being (we will come back to it later) and let's just say that this tells both the system as well as the user that we should assume that the value of the balance field has changed when our bank account signals a #valueChanged event. Adding these annotations for virtual fields is important (for regular fields they are added automatically) but nothing you should worry about until you've understood a little more about virtual fields and what they are good for.

With balance being a virtual field, we now use the value field to store the account balance. Note that we did not have to change any of existing code which uses balance which is one of the advantages of virtual fields - they effectively allow us "rename" fields with minimal efforts.

However, having said how cool virtual fields are I should also add that I see the above usage as a double-edged sword. In many situations, "aliasing names" as we just did can lead to confusion - what if someone later comes and says "no, this shouldn't be called value, it should be called funds" and later comes another name and then yet another and by the end of the day we really don't know what the thing actually is being called.

In other words: Use virtual fields for renaming with caution. In many cases simple refactoring (e.g., really renaming all the places where it is being used) is a better alternative.

Back to top


Making the ATM

Now that we've completed the bank account class, it is time to make us a user interface for it - such as an ATM. The ATM should display the balance of our account and it should allow us to deposit and withdraw some money. For this, we'll need the following fields:

Since all graphical objects in Tweak are players, we should make the ATM a subclass of CPlayer, e.g.,

CPlayer subclass: #ATM
	instanceVariableNames: 'account balanceReadout depositButton withdrawButton'
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Tutorial'

Back to top


Defining the UI elements

Since the account will be provided from elsewhere, we only need to initialize the user interface elements. For doing this, add a new method category "initialize" and enter the following code:

initialize
	"Initialize the receiver"
	super initialize.
	self define: #balanceReadout as: CLabel new.
	self define: #depositButton as: CButton new.
	self define: #withdrawButton as: CButton new.

The use of #define:as: signifies another important relation in Tweak: Functional ownership. This relation is independent of the graphical composition of objects (e.g., embedding hierarchy) and models the functional relationship between objects. In this example, we wish to express that regardless of what it is and where it is, the ATMs depositButton functionally acts in the role of "being the button which one can use to deposit money in this ATM" regardless of where it is on the screen.

Since Tweak allows arbitrary and interactive assembly and disassembly of objects, expressing the functional relations between objects is both important for the system to maintain its integrity as well as the user to understand "what this thing is". This is why the functional ownership is shown in halo labels, e.g., it will read as "ATM's depositButton".

Hopefully, this information will be generated in the future automatically by appropriate UI building tools. For now, the rule of thumb is: All sub-widgets in players should be #defined:as:. Other attributes (such as our account) should not.

Back to top


Setting up the costume

In Tweak, all players wear costumes. Costumes define the appearance of a player, they can be simple or complex, they can be passive or highly functional.

ADVANCED: Note that being a costume is a role rather than a type; any player can act as a costume for another player. Without going into details here let's simplify this somewhat by saying: You do not have to make a subclass of CCostume to be a costume; any player can be the costume for other players. For example, you might do things such as:

	player := CPlayer new.
	player costume: CEllipsePlayer new.
	player open.
to give some player a costume which looks like an ellipse and indeed is just another player. And yes, costumes can have costumes too, ad infinitum.

Since costumes are real objects it is often useful to avoid their creation up to the point where the costume is really needed. Because accessing any of the graphical attributes (position, color, etc) will cause the costume to be created most of the graphical setup for players should happen in the method #setupCostume which is used by the system when the costume has to be created.

For setting up our ATM, enter and accept the following method:

setupCostume
	"Setup this ATM's costume"
	super setupCostume.

	"setup ATM itself"
	self extent := 100@50.
	self color := Color white.

	"setup the readout"
	balanceReadout bounds := 0@0 extent: 100@25.
	self add: balanceReadout.

	"setup the deposit button"
	depositButton label := '+50'.
	depositButton bounds := 0@25 extent: 50@25.
	self add: depositButton.

	"setup the withdraw button"
	withdrawButton label := '-50'.
	withdrawButton bounds := 50@25 extent: 50@25.
	self add: withdrawButton.

To test whether the ATM looks correct, execute the following code from a workspace:

	ATM open.

Back to top


Displaying the balance

Now that we have the account and the ATM, let's run a real test

	atm := ATM open.
	atm account: BankAccount new.

As expected, this isn't quite working yet. The first thing we need to do is to update the balance display when we give the ATM a new account to work with. However, rather than overriding the #account: message and updating the balance readout directly, we will use a change event.

Change events are a powerful concept in Tweak. Whenever a field changes its value, it will generate an event that interested parties can react to asynchronously. In other words, rather than messing with the implementation of the #account: message we can update the balance when we get informed that the account indeed has changed.

In order to do this, we need to find out which event is being generated and how to make this event trigger the appropriate reaction. For the first task (finding out which event is generated) we need to look at the ATM's #account method: Here we will find the annotation <bewareOf: #accountChanged> which tells us that we will see an #accountChanged event when the value of this field changes. As you might guess by now, all of the regular fields have a simple pattern: A field named <foo> will generate <foo>Changed events so it is very simple to guess the event name (you still have to look for the event when you are dealing with virtual fields, though!).

For the second step we need to know how to associate the event with the reaction that we wish to execute. There are various ways of doing this, by far the most recommended is to use a method trigger.

A method trigger is an annotation that tells the system to execute a method when a specific event is signaled. Method triggers are expressed by <on: eventName> or <on: eventName in: signaler> annotations. For associating a reaction with the #accountChanged event, we can write the following method:

onAccountChanged
	"Update the balance readout when the account changes"
	<on: accountChanged>
	balanceReadout value: account value.

After accepting the method, the ATM will indeed display the value (balance) of the account whenever we give it a new account.

Note that methods with event triggers should generally adhere to the naming convention that can be seen in the above - all event-triggered methods should read onEventName so that the casual reader is aware that this method is indeed being triggered by an event and so that it is easy to find out which event is being reacted to.

More complex event reactions that might be invoked from other places should generally be factored out in two methods: One method containing the trigger and being named after the event, and another one containing the reaction and being named after the goal. In our example, this would look like:

onAccountChanged
	"Update the balance readout when the account changes"
	<on: accountChanged>
	self updateBalanceReadout

updateBalanceReadout
	"Update the balance readout from the account"
	balanceReadout value: account value.

The first method (containing the event trigger) is named after the trigger (onAccountChanged) the second method is named after the goal (updateBalanceReadout). For making it easier to understand the system, event triggered methods should generally not be invoked directly (e.g., seeing a "self onFoo" anywhere is considered Extremely Bad Style).

Back to top


Watching changes in the account

Since we are a networked society by now, we want our information to be as up-to-date as possible. Someone might just have transferred funds into our account and we'd like the ATM to reflect the change as soon as possible. Say, something like:

	account := BankAccount new.
	account deposit: 50. "deposit some funds"
	atm := ATM open.
	atm account: account.
	self wait: 1. "wait for a second"
	account deposit: 50. "and deposit even more"

This will still display "50" although more funds are in the account when we have executed the code. What we need to do to fix this problem is to tell the ATM to update the display when the account's value changes. This is just like our previous problem except that we need to tell the ATM that the event is really signaled by the account:

onAccountValueChanged
	"Update the balance readout when the account value changes"
	<on: valueChanged in: account>
	self updateBalanceReadout.

The <on:in:> annotation allows us to both, specify the event as well as the signaler of the event. After accepting the above we can now play some fun games watching the ATM as we deposit and withdraw funds from the account:

	account := BankAccount new.
	atm := ATM open.
	atm account: account; waitTick.
	account deposit: 50; wait: 1.
	account deposit: 50; wait: 1.
	1 to: 10 do:[:i| account withdraw: 10; wait: 0.1]

Back to top


Making the buttons work

Now that we're almost done with our ATM let's finish up with the buttons. As a matter of fact the only thing that you need to know about buttons and that hasn't been mentioned, is that by definition, buttons fire. In other words, they generate a #fire event when their associated action should be executed. This makes it fairly straightforward to define our button actions:

onDepositButtonFired
	"Deposit money"
	<on: fire in: depositButton>
	account deposit: 50.

onWithdrawButtonFired
	"Withdraw money"
	<on: fire in: withdrawButton>
	account withdraw: 50.

And now we are ready to use our ATM.

Back to top


Summary

This is the end of the tutorial. I hope you were having fun and got some insights into Tweak and how to use it. Keep in mind that this really isn't an in-depth discussion so many things can only be touched briefly and would require more time to talk about.

Back to top