;; This file contains excerpts from the textbook Concrete ;; Abstractions: An Introduction to Computer Science Using Scheme, by ;; Max Hailperin, Barbara Kaiser, and Karl Knight, Copyright (c) 1998 ;; by Brooks/Cole Publishing Company. This file may not be reproduced ;; or redistributed other than for use with that textbook. ;; Chapter 14: Object-Oriented Programming ;; 14.1 Introduction ;; 14.2 An Object-Oriented Program (define-class 'item-list ; the class is named item-list object-class ; it has the object-class as its superclass '(item-vector ; it has instance variables named item-vector num-items) ; and num-items '(add ; and methods with these names display total-price delete choose empty?)) (class/set-method! item-list-class 'empty? (lambda (this) (= (item-list/get-num-items this) 0))) (define example-item-list (make-item-list)) (class/set-method! item-list-class 'init (lambda (this) (item-list/set-item-vector! this (make-vector 10)) (item-list/set-num-items! this 0))) (item-list/get-num-items example-item-list) ;Value: 0 (item-list/empty? example-item-list) ;Value: #t (object/describe example-item-list) An instance of the class item-list with the following instance variable values: num-items: 0 item-vector: [a 10 element vector] class: [an object of class class] (class/set-method! item-list-class 'add (lambda (this item) (let ((num-items (item-list/get-num-items this)) (item-vector (item-list/get-item-vector this))) (if (= num-items (vector-length item-vector)) (begin ; some code (yet to be determined) goes here to ; replace the vector with a bigger one somehow (item-list/add this item)) (begin (vector-set! item-vector num-items item) (item-list/set-num-items! this (+ num-items 1)) 'added))))) (define-class 'item-list object-class '(item-vector num-items) '( ;; intended for public consumption: add display total-price delete choose empty? ;; intended for private, internal use: grow )) (class/set-method! item-list-class 'add (lambda (this item) (let ((num-items (item-list/get-num-items this)) (item-vector (item-list/get-item-vector this))) (if (= num-items (vector-length item-vector)) (begin (item-list/grow this) (item-list/add this item)) (begin (vector-set! item-vector num-items item) (item-list/set-num-items! this (+ num-items 1)) 'added))))) (class/set-method! item-list-class 'display (lambda (this) (let ((num-items (item-list/get-num-items this)) (item-vector (item-list/get-item-vector this))) (from-to-do 0 (- num-items 1) (lambda (index) (display (+ index 1)) (display ") ") (item/display (vector-ref item-vector index)) (newline)))) (display "Total: ") (display-price (item-list/total-price this)) (newline) 'displayed)) (class/set-method! item-list-class 'choose (lambda (this) (if (item-list/empty? this) (error "Can't choose an item when there aren't any.") (begin (display "Which item?") (newline) (item-list/display this) (vector-ref (item-list/get-item-vector this) (- (input-integer-in-range 1 (item-list/get-num-items this)) 1)))))) (define input-integer-in-range (lambda (min max) (display "(enter ") (display min) (display "-") (display max) (display ")") (newline) (let ((input (read))) (cond ((not (integer? input)) (display "input must be an integer and wasn't") (newline) (input-integer-in-range min max)) ((or (< input min) (> input max)) (display "input out of range") (newline) (input-integer-in-range min max)) (else input))))) (define-class 'item object-class '(price) '( ;; intended for public consumption: price display input-specifics revise-specifics)) (define example-item (make-item 1950)) ; 1950 cents = $19.50 (class/set-method! item-class 'init (lambda (this price) (item/set-price! this price))) (define-class 'special-item item-class '() '()) (class/set-method! special-item-class 'input-specifics (lambda (this) (newline) (display " *** doing special input ***") (newline))) (define a-normal-item (make-item 1950)) (define a-special-item (make-special-item 1000000)) (item/input-specifics a-normal-item) (item/input-specifics a-special-item) *** doing special input *** (item/revise-specifics a-normal-item) (item/revise-specifics a-special-item) *** doing special input *** (define-class 'oxford-shirt item-class '(color neck sleeve specified-yet) '( )) (define an-oxford-shirt (make-oxford-shirt)) (item/price an-oxford-shirt) ;Value: 1950 (oxford-shirt/get-specified-yet an-oxford-shirt) ;Value: #f (class/set-method! oxford-shirt-class 'init (lambda (this) (item/set-price! this 1950) (oxford-shirt/set-specified-yet! this #f))) (class/set-method! oxford-shirt-class 'init (lambda (this) (item/init this 1950) (oxford-shirt/set-specified-yet! this #f))) (class/set-method! oxford-shirt-class 'init (lambda (this) (item^init this 1950) (oxford-shirt/set-specified-yet! this #f))) (class/set-method! oxford-shirt-class 'display (lambda (this) (if (oxford-shirt/get-specified-yet this) (begin (display (oxford-shirt/get-color this)) (display " Oxford-cloth shirt, size ") (display (oxford-shirt/get-neck this)) (display "/") (display (oxford-shirt/get-sleeve this)) (display "; ")) (display "Oxford-cloth shirt; ")) (item^display this))) (let ((item-list (make-item-list))) (item-list/add item-list (make-item 100)) (item-list/add item-list (make-oxford-shirt)) (item-list/add item-list (make-item 200)) (item-list/add item-list (make-item 300)) (item-list/add item-list (make-oxford-shirt)) (item-list/display item-list)) (class/set-method! oxford-shirt-class 'input-specifics (lambda (this) (display "What color?") (newline) (oxford-shirt/set-color! this (input-selection '("Ecru" "Pink" "Blue" "Maize" "White"))) (display "What neck size? ") (oxford-shirt/set-neck! this (input-integer-in-range 15 18)) (display "What sleeve length? ") (oxford-shirt/set-sleeve! this (input-integer-in-range 32 37)) (oxford-shirt/set-specified-yet! this #t) 'inputted)) (define input-selection (lambda (choices) (define display-loop (lambda (number choices) (if (null? choices) 'done (begin (display " ") (display number) (display ") ") (display (car choices)) (newline) (display-loop (+ number 1) (cdr choices)))))) (display-loop 1 choices) (list-ref choices (- (input-integer-in-range 1 (length choices)) 1)))) (define compu-duds (lambda () (let ((item-list (make-item-list))) (define loop (lambda () (newline) (display "What would you like to do?") (newline) (display " 1) Exit this program.") (newline) (display " 2) Add an item to your selections.") (newline) (display " 3) List the items you have selected.") (newline) (display " 4) See the total price of the items you selected.") (newline) (let ((option (if (item-list/empty? item-list) (input-integer-in-range 1 4) (begin (display " 5) Delete one of your selections.") (newline) (display " 6) Revise specifics of a selected item.") (newline) (input-integer-in-range 1 6))))) (newline) (cond ((= option 2) (let ((item (input-item))) (item-list/add item-list item) (item/input-specifics item))) ((= option 3) (item-list/display item-list)) ((= option 4) (display-price (item-list/total-price item-list)) (newline)) ((= option 5) (item-list/delete item-list (item-list/choose item-list))) ((= option 6) (item/revise-specifics (item-list/choose item-list)))) (if (not (= option 1)) (loop))))) ;end of the loop procedure (loop)))) ;this starts the loop (define input-item (lambda () (display "What would you like?") (newline) (display " 1) Chinos") (newline) (display " 2) Oxford-cloth shirt") (newline) (if (= (input-integer-in-range 1 2) 1) (make-chinos) (make-oxford-shirt)))) (define-class 'chinos item-class '(color size ; waist, in inches inseam ; also in inches cuffed ; #t = cuffed, #f = hemmed specified-yet) '( )) (class/set-method! chinos-class 'init (lambda (this) (item^init this 3300) ; chinos are priced at $33.00 (chinos/set-specified-yet! this #f))) (class/set-method! chinos-class 'display (lambda (this) (if (chinos/get-specified-yet this) (begin (display (chinos/get-color this)) (display " chinos, size ") (display (chinos/get-size this)) (display ", ") (display (if (chinos/get-cuffed this) "cuffed" "hemmed")) (display " to ") (display (chinos/get-inseam this)) (display " inches; ")) (display "Chinos; ")) (item^display this))) (class/set-method! chinos-class 'input-specifics (lambda (this) (display "What color?") (newline) (chinos/set-color! this (input-selection '("Charcoal" "Khaki" "Blue"))) (display "What waist size? ") (chinos/set-size! this (input-integer-in-range 30 44)) (display "Hemmed or cuffed?") (newline) (display " 1) Hemmed") (newline) (display " 2) Cuffed") (newline) (chinos/set-cuffed! this (= (input-integer-in-range 1 2) 2)) (display "What inseam length? ") (chinos/set-inseam! this (input-integer-in-range 29 (if (chinos/get-cuffed this) 34 36))) (chinos/set-specified-yet! this #t) 'inputted)) ;; 14.3 Extensions and Variations (show-class-hierarchy) ; object ; item-list ; item ; chinos ; oxford-shirt ; class (object/describe chinos-class) ; The class chinos has the following ancestry: ; object ; item ; chinos ; and the following immediate subclasses: ; and the following instance variables (including inherited ones): ; specified-yet (new) ; cuffed (new) ; inseam (new) ; size (new) ; color (new) ; price (from item) ; class (from object) ; and the following method names (including inherited ones): ; revise-specifics (name from item, implementation from item) ; input-specifics (name from item, new implementation) ; display (name from item, new implementation) ; price (name from item, implementation from item) ; init (name from object, new implementation) ; describe (name from object, implementation from object)