summaryrefslogtreecommitdiff
path: root/examples/ootest.tcl
blob: 731e46a6073bb60c37cce4b4bcfad4ad4ef4d708 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
package require oo

# Create a class, the usual bank account, with two instance variables:
class Account {
	balance 0
	name "Unknown"
}

# We have some class methods predefined
# Note we can call (e.g.) either Account.methods or 'Account methods'
puts "---- class Account ----"
puts "Account vars=[Account vars]"
puts "Account methods=[Account methods]"
puts ""

# Create a constructor. This does validation, but it could
# do other things
Account method constructor {} {
	if {$balance < 0} {
		error "Can't initialise account with a -ve balance"
	}
}

# Now flesh out the class with some methods
# Could use 'Account method' here instead
Account method deposit {amount} {
	set balance [+ $balance $amount]
}
Account method see {} {
	set balance
}
Account method withdraw {amount} {
	if {$amount > $balance} {error "Sorry $name, can only withdraw $balance"}
	set balance [- $balance $amount]
}
Account method describe {} {
	puts "I am object $self of class [$self classname]"
	puts "My 'see' method returns [$self see]"
	puts "My variables are:"
	foreach i [$self vars] {
		puts "  $i=[set $i]"
	}
}

# Now an instance, initialisition some fields
set a [Account new {name "Bob Smith"}]

puts "---- object Account ----"
# We can use class methods on the instance too
puts a.vars=[$a vars]
puts a.classname=[$a classname]

# Now object methods
$a deposit 100
puts "deposit 100 -> [$a see]"

$a withdraw 40
puts "withdraw 40 -> [$a see]"

catch {$a withdraw 1000} res
puts "withdraw 1000 -> $res\n"

# Tell me something about the object
$a describe
puts ""

# Now create a new subclass
# Could change the initial balance here too
class CreditAccount Account {
	limit -1000
}

CreditAccount method constructor {} {
	# Dummy constructor
	# If desired, manually invoke the baseclass constructor
	super constructor
}

# Override the 'withdraw' method to allow overdrawing
CreditAccount method withdraw {amount} {
	if {$balance - $amount < $limit} {error "Sorry $name, that would exceed your credit limit of [expr -$limit]"}
	set balance [- $balance $amount]
}
# Override the 'describe' method, but invoke the baseclass method first
CreditAccount method describe {} {
	# First invoke the base class 'describe'
	super describe
	if {$balance < 0} {
		puts "*** Account is in debit"
	}
}

puts "---- class CreditAccount ----"
puts "CreditAccount vars=[CreditAccount vars]"
puts "CreditAccount methods=[CreditAccount methods]"
puts ""

puts "---- object CreditAccount ----"
set b [CreditAccount new {name "John White"}]

puts b.vars=[$b vars]
puts b.classname=[$b classname]

puts "initial balance -> [$b see]"
$b deposit 100
puts "deposit 100 -> [$b see]"

$b withdraw 40
puts "withdraw 40 -> [$b see]"

$b withdraw 1000
puts "withdraw 1000 -> [$b see]"
puts ""

# Tell me something about the object
$b describe
puts ""

# 'eval' is similar to 'dict with' for an object, except it operates
# in it's own scope. A list of variables can be imported into the object scope.
# It is useful for ad-hoc operations for which it is not worth defining a method.
set total 0
$a eval total { incr total $balance }
incr total [$b get balance]
puts "Total of accounts [$a get name] and [$b eval {return "$name (Credit Limit: $limit)"}] is: $total"

# Can we find all objects in the system?
# Almost. We can't really distinguish those which aren't real classes.
# This will get all references which aren't simple lambdas.
puts "---- All objects ----"
Account new {name "Terry Green" balance 20}
set x [Account]
lambda {} {dummy}
ref blah blah

foreach r [info references] {
	if {[getref $r] ne {}} {
		try {
			$r eval {
				puts [format "Found %14s: Owner: %14s, Balance: %+5d, in object %s" [$self classname] $name $balance $self]
			}
		} on error msg {
			puts "Not an object: $r"
		}
	}
}
unset r

# And goodbye
$a destroy

# Let the garbage collection take care of this one
unset b
collect