TweetFollow Us on Twitter

Design Objects 2
Volume Number:7
Issue Number:11
Column Tag:Programmer's Forum

Designing Objects

By Dave Curtis, Sunnyvale, CA

Note: Source code files accompanying article are located on MacTech CD-ROM or source code disks.

“Reusable software components” -- that catch phrase gets a lot of use by proponents of object oriented languages. True reusability, however, is more easily achieved in advertising than in real world code. It is possible to design tangled and tortured objects, just as it is possible to write spaghetti code. Good object design requires a new way of thinking.

Some time ago, I started a project to create core objects for common data structures. After all, aren’t you tired of creating (and debugging) a linked list, a queue, a binary tree, etc., for the umpteenth time? I am, for sure. My experience taught me a few lessons, which I have reduced to a set of object design guidelines. While I can’t claim to be a guru of object design, these thumb rules provide a simple, structured approach to designing objects that work well in real world code.

All of the examples shown here are written in MPW Pascal, but you C++ fans shouldn’t turn the page yet. The object design principals apply to any object oriented system, and all of the objects are simple enough that they should be easy to translate.

Object Design Guidelines

The goal of these guidelines is to keep all objects small, simple and re-usable. Small, simple objects are easier to understand and easier to debug. Small, simple objects have less excess baggage and fewer undesirable side effects which make reuse difficult. If, while trying to use an object as the foundation for a more complex structure, you find that the object has too much overhead or does work in excess of your needs, then you have violated the first guideline:

Factor objects by data and operation.

What does it mean to factor an object? Most data structures of any meaningful size are made up of a collection of simpler pieces. With objects, we can carry that statement further and say that the methods are also a collection of simpler pieces. A well factored object has been broken into a collection of smaller, simpler objects that have easy to understand functions. Easily said, but what identifies a good place to start factoring? Guideline two:

Make object families for complex data structures.

All complex data structures can be viewed as a “whole” made up of “parts”. In conventional programming languages the distinction between these two perspectives blurs, and we become used to submerging the personalities of these two views. This is especially true for pointer based structures, where we define a single record type and root the entire structure with a single pointer variable of the record type. In object programming, it pays to think carefully about this distinction, and to create separate object types for the “whole” and the “part”. In an example shown later, a linked list is factored into list-element objects (the part) and list objects (the whole). Both data and methods are then factored by answering the question: “Does this apply to the whole or to the part?”

Many operations on complex data structures require that the elements be traversed. Traversal operations are a good place to factor. Traversal methods, and any data required, can be placed in a separate object as part of the object family used to make up the complex data structure. A family of whole/part/traversal object types is sufficient for many data structures. In fact, if you don’t find this triad in your object family, you should ask yourself why.

Often, the traversal objects will only be used by methods of the “whole” object, and will not be used by outside clients of the object family. It still makes sense to create a separate object in order to minimize the side effects that occur when creating descendant types of any member of the object family. This is really another facet of the third guideline:

Consider the legacy.

After all, our avowed goal is to create reusable objects. After factoring a data structure into a workable family of sub-objects, it is time to consider how each individual object type will be used by its descendants. A good job of factoring into sub-objects and factoring methods among objects can often be accomplished by considering only the data fields. This first pass of factoring, however, often leads to methods that are too large. The “whole/part” question will help us apply a method to the correct object, but doesn’t address issues of inheritance.

To frame your thinking, ask: “How would a descendant object use this method in an INHERITED call?” Depending on the form of the answer, the method might need to be factored into two or more simpler methods attached to the same object. For each step of each method, ask if a descendant wants to inherit or override that step. Method inheritance usually has one of the following patterns:

1) Call to INHERITED method, followed by descendant code (mnemonically: Ic).

2) Descendant code, followed by call to INHERITED method (cI).

3) Some descendant code, an INHERITED call, some more code (cIc).

4) Some INHERITED work, descendant code, more INHERITED work (IcI).

Patterns 1 and 2 (Ic and cI) are great -- no change needed. Initialization methods often have the Ic pattern, and destruction methods often have the cI pattern. If this isn’t true, the method in question should be examined closely to make sure that a deviation from the normal pattern makes sense. Pattern 3, cIc, is less frequent, but is no less natural. The IcI pattern, however, is another story. The IcI pattern is impossible to code in Pascal and indicates the need for better method factoring.

An IcI pattern arises when generic setup and cleanup work surround type specific actions. There are two approaches to factoring the IcI pattern. One approach is to split the method into two smaller methods, and let descendant types call each separately. This is usually the wrong approach. More often than not, a second generation descendant will have the same problem -- another IcI pattern, only nested this time: I2IcII2. Yuk!

A better approach is to factor the offending method by adding one or more auxiliary methods. The additional method(s) do type specific work, and the original method does the generic setup and cleanup, calling the additional method in between. The additional methods often do very little or even nothing in the foundation object. That’s OK, they are designed to be overloaded. Remember, you can’t overload a method that isn’t there to begin with, so dummy methods are an important part of the foundation. A dummy method provides a clean way for descendant objects to expand the interior of an inherited procedure.

Recognizing and factoring potential IcI patterns is the most difficult part of object design, since to a large extent it requires predicting the future. A simple and innocuous method can easily hide potential IcI problems that won’t show up for most uses of the object. There is no substitute for careful thought. Trace each method step by step. Keep a watchful lookout for validity checks and error handlers, as descendant objects often need to extend them. Give the boolean expression in every test double scrutiny. Break the expression out as a new boolean function method if there is any chance that a descendant might wish to extend the test.

Guideline Summary

Here are the guidelines in summary form:

1) Factor objects by data and operation.

2) Make object families.

a) object for the whole

b) object(s) for the parts

c) object(s) for traversal

Ask: Does method belong to whole or part?

3) Consider the legacy.

a) Examine inheritance patterns for soundness.

b) Provide dummy methods.

Ask: Will this method need interior expansion?

Linked List Example

The linked list data structure unit shown in listing 1 is a prefect illustration of the synergy of object families. Object factoring follows the whole/part/traversal pattern. The “whole” object, listObj, contains the head link of the list, and performs operations that apply to the list as an aggregate data structure. The “parts” are defined by listElementObj, which contains the chaining link and element specific operations. Traversal is provided by the object listIterator, which walks the list from head to tail.

Nowhere in any of the objects is there any space to store data! These objects are designed to be used as foundation building blocks. Descendant types append data fields as needed. The objects defined by the unit have been boiled down to “essence of linked list”. In other words, the linked list concept itself has been factored into “data” and “structure”, and these objects provide the “structure”, while the client defines the “data”. Here is what the objects do:

ListElementObj is the basic building block used for lists. As mentioned above, the only field it defines is the link pointer, which in this case is an object variable of type listElementObj. The init method sets this link to nil, and has been designed for an Ic inheritance pattern. The release method is a do-nothing place holder. Descendant types that define data fields referencing heap storage should override this method.

ListObj defines a list as a whole entity. Again there is no data, just a head link. The init method clears the head link. ListObj.release disposes all of the list elements as well as the list object itself, making use of the dummy method listElementObj.release. ListObj.release illustrates the importance of factoring for maximum reusability. ListObj.release is itself designed for a cI inheritance pattern, and provides for a descendant call back to avoid the dreaded and impossible IcI pattern via listElementObj.release. Without the dummy method listElementObj.release, descendant objects would not be able to cleanly release storage. In anticipation of the potential that some descendant types may keep multiple references to a single list element, listElementObj.release is defined as a boolean function so that the result of a reference count check can be returned to ListObj.release.

So far, all the methods we have talked about exist only for housekeeping. Real linked list operations are provided by the methods append, appendList, insert, and head. Append and insert add a listElementObj to either the tail or the head of the list. AppendList adds another list to the end of the list, and disposes of the storage associated with the first list header. Head nips the first element off the list, returning it through a var parameter.

The head method illustrates one of the severe deficiencies of Pascal as an object oriented programming language. Ideally, head would be a function, not a procedure, and would return a descendant object type. Unfortunately, Pascal provides no mechanism to return a value having a descendant type. We could return a listElementObj, but in that case the caller always needs to perform a type coercion, causing the calling expression to get messy very quickly.

Object data structure factoring often leads to legitimate “uphill” type coercions like these. My (rather ugly) solution is to use var...univ parameters and programmer self discipline. Descendant objects could wrap the var parameter procedure call in a nice looking function, but I prefer to avoid the overhead. This might also be a good place to use the built-in member function, to make sure that the parameter is a descendant of listElementObj.

The listIterator object is built on a very simple underlying object type, iterator. Listing 2 shows the iterators unit. The iterator object provides a set of common properties for data structure traversal objects. An iterator defines the state information and methods necessary to visit each element of a complex data structure exactly once. When an iterator is used to perform an operation on the aggregate data structure there is usually one instance of an iterator variable per loop nesting level.

The basic iterator object contains fields named subject and i. Subject is the aggregate object being traversed (the “whole”), i is the current element node being visited (current “part”). The init method establishes a connection between the iterator and the traversed object. LoopReset causes the iterator to start over, no matter what the current state. The another method does all of the real work, but in the basic iterator, is just a dummy. Since another needs knowledge of the subject data structure, it must be provided by a descendant type that is part of a data structure family. Notice, however, that client code using the descendant iterator need understand neither the iterator nor the data structure being traversed! Let’s take a closer look:

Suppose we have a complex data structure called mysteriousStuff, made up of mysteriousElements. We want to send method doItToIt to every element of mysteriousStuff. We have an iterator called doingItTo that can traverse mysteriousStuff. We do the following:


 while doingItTo.another(mysteriousElement) do

This code works for any data structure of any kind of object. The only requirement is the existence of an another method with understanding of the data structure.

In practice, it turns out that most iterator based loops make one pass over the structure sending the same message to each object. Suppose we could design a method named forAll that had a method parameter, something like:


 mysteriousStuff = object (venerableAncestor)
 procedure forAll 
 (procedure mysteriousElement.m);

The client code could pass in any parameterless method, and forAll could send it to every element of the data structure. This would be simple and elegant from the client code point of view, and often simpler to implement than an iterator object. Unfortunately, MPW Pascal won’t accept method parameter declarations. Apple, if you’re listening, please fix this omission.

With that short digression to discuss iterators, we can complete the lists unit. We simply need to define the listIterator.another method. ListIterator marches down the list from head to tail. The variable i provides sufficient state to keep track of both the reset condition and the mid-loop condition, so listIterator adds no new fields. Once again, we need a couple of uphill type coercions, and might consider screening both the parameter anObject and the field subject with member.

Does the Lists Unit Meet the Guidelines?

The first guideline advocates factoring objects by data and operation. ListObj data roots a list, and provides methods that treat lists as a whole. ListElementObj defines element oriented features. The objects have been factored.

Guideline number two suggests an object family, with members for the whole, the parts, and traversal. The lists unit fits the model exactly.

Finally, guideline number three admonishes us to be aware of inheritance patterns. Because of that, we have carefully factored the release methods to provide for safe destruction of descendant data. Note also that listObj.appendList is careful to use release to destroy the extra listObj, even though within the lists unit a simple dispose would be sufficient. If appendList used dispose directly there would be no opportunity for a descendant of listObj to release its storage.

The Lists Unit in Action

Listing 3 shows testLists, a simple program that puts the lists unit through its paces. It creates descendant types of both listObj and listElementObj, and makes use of the listIterator object. TestLists also uses one other unit we haven’t talked about, the memCheck unit.

One of the nasty realities of life is that new(someObject) won’t always work. Life gets even nastier if we ignore the occurrence of this unwelcome event. The memCheck unit shown in listing 4 provides bare-bones handling for out-of- memory conditions, via an object that can be extended when more sophisticated actions are required. It is a good illustration of how functional factoring is useful even for objects that have trivial data structures.

The memCheck unit defines an object type called memCheckObj. All of the functionality required to deal with an out of memory condition is collected under this object. The key method is memCheckObj.gotObject, which checks for an empty object, invokes garbage collection if needed, and takes an error exit on failure. To use memCheck, all object creation calls using new should be written like this:


 new (anObject)
 until memCheck.gotObject(anObject);

All error trapping clutter is moved out of the client code and into the methods of memCheckObj. The three steps of checking for a valid object -- nil handle test, garbage collection and error exit -- are factored into separate methods. Client code can override memCheck.collectGarbage with a method that understands the application’s data structures. MemCheck.outOfMemExit provides a place to get a “good-bye kiss” so that clean-up work can take place before returning to the shell.

The testLists program illustrates how to use memCheck, and also shows a very simple linked list. Each list element has an integer data cell, which is defined in testListElementObj. The method dump prints it out. TestListObj also has a dump method, which prints out the entire list. TestListObj.dump is a good illustration of an iterator object in action.

TestLists shows that the lists unit works, and shows how to use it to create a stand-alone linked list. If we’ve done our job well, we should also be able to use the lists unit as a building block for other more complex data structure units. We still apply the same object design guidelines to our new structures. Now, however, we have the lists unit in our code inventory so linked list functionality can be had without tedious re-invention. In the next section we do just that -- the lists unit is the foundation for a stack data structure object family.

The Ultimate Stack

Stacks are a very useful data structure, but often a thorny storage allocation issue. Allocating a fixed amount of storage implies not only a hard limit on the maximum depth of the stack, but also requires that the program allocate enough storage to contain the maximum expected stack. The excess space is often wasted. It is true that a stack can be implemented as a linked list, but a linked list implementation is not a very efficient use of space because the linked list overhead is so large compared to the data. The optimum stack would use an efficient, but expandable data structure.

One way to make a stack expandable is to break the stack into frames. Each frame is mini-stack with a moderate number of cells. When one frame overflows, another frame is added to the structure. The stack can grow as large as heap space allows, but at no time does the stack consume greatly more space than required.

Can we derive an efficient stack data structure using the idea of segmented stack frames? Let’s apply the object design guidelines to this concept and see where they lead.

The client program’s view of a stack is that it is a place to put things for retrieval in last-in-first-out (LIFO) order. What are the “things”? Since we are object oriented programming fanatics, the things are objects, of course. The commonly accepted stack operations are ‘push’, which adds an object to the top of the stack, and ‘pop’, which retrieves an object from the top of the stack. Cheaters also sometimes use the ‘pick’ operation, which reaches down into the stack for an object.

The first object design guideline tells us to factor operations and data. But a stack provides such a limited view of its contents that the clients view of a stack is all operations! That is easy to factor. We can serve a client program’s needs with an object having push, pop and pick operations and no client visible data. Data representation details can be completely hidden in another object without loss of functionality. This is good, since it means our stack frame scheme can easily be made transparent to the client program.

We already have a family of objects, so the second guideline is at least partially met. We have a client visible stack object, and a potentially private implementation object. We know object families often consist of whole/part/iterator triads. Do we need an iterator for the stack family? Under normal operation, a stack is rarely swept with a loop. One could argue that the stack is a data structure that needs no iterator. On the other hand, it is handy to be able to dump the contents of the stack during debugging, so for completeness we should toss in an iterator.

The third guideline causes us to pause and think about how derivative objects will use this object family. One thing that comes to mind is that stacks are usually homogeneous; in other words, all elements of a stack have the same type. To be generic, our stack structure needs to allow any type of object to be pushed. A client program might wish to limit this “open door” policy. One way a client program could screen pushes would be to override ‘push’ with a type screening method making use of the object Pascal built-in member function. A cleaner solution is to let the generic push do the screening, but factor out the type screening function as a separate method. For completeness, we can also provide an error handling method to be called when the screen fails.

Let’s review the design decisions so far: We have the familiar whole/part/iterator object family. The client interface routines provided by the stack are push, pop, pick, a push screening function (let’s call it okToPush), and a procedure to handle failed screens (we’ll call it rejectPush). The stack element object is transparent to the user. The stack iterator provides generic functionality. Looks like we are ready to implement the stack.

As mentioned above, the stack will be implemented as a group of frames. A linked list is a perfect way to manage the frames, since the linked list will keep the frames in order, and since most stack accesses use the most recently created frame. Luckily, we happen to have an object family that implements linked lists already, so we can build the stack object family on top of the lists unit.

Listing 4 shows the completed stacks unit. The stackObj type is built on the listObj type. All of the new client interface methods are attached to stackObj. Pop and pick are implemented as procedures using var...univ parameters rather than as functions, because, as discussed earlier, object Pascal lacks sufficient type inheritance semantics. StackFrameObj is a descendant of listElementObj. Since stackFrameObj contains a field that must be initialized, stackFrameObj overrides the init method. StackSweepObj is a descendant of the generic iteratorObj, not listIteratorObj, because stackSweepObj needs to visit every cell in the stack, not just every frame.

All of the stack objects are clean descendants of their respective ancestor objects. All of the overridden methods are ones we expected to override, and the override methods inherit ancestor functionality as expected. This is good verification of sound design in the linked list object family.

Stack Implementation High Lights

The stacks unit has been designed so that the push method only adds a frame when the current frame overflows, and pop only discards an empty frame on under flow. This minimizes thrashing near frame boundaries. Still, push and pop are almost as simple as in a normal stack implementation. There is a small amount of extra code to insert and delete frames, and of course push calls the dummy okToPush method which allows descendant objects to define type screening.

Because the stack is split into frames, pick is more complex than in the usual stack implementation. Pick first calculates how many frames it must walk back to find the requested element, walks to the correct frame, then selects the correct element from within that frame.

StackFrameObj adds two fields to its base type, listElementObj. An array called cell holds objects pushed onto the stack, and framePointer indicates the topmost element of the local frame. Since framePointer must be initialized, stackFrameObj overrides listElementObj.init.

Stack Operational Details

As mentioned above, push only adds a frame when the current frame overflows. This condition happens in one of two ways, either the stack is completely empty, or the topmost frame of the stack is completely full. In either case, a new frame is inserted at the head of the frame list. Since each frame is a listElementObj the actual list insertion is done by an inherited method.

OkToPush guards the entire push operation. The default okToPush method always returns true, allowing any type of object (or in truth, any long word) into the stack. Descendant objects can override it to create selective stacks. The companion method rejectPush doesn’t do anything, so unless descendant objects override rejectPush along with okToPush, rejected objects will disappear almost without a trace. Unfortunately, the storage referenced by the rejected object handle will never be freed unless rejectPush is augmented to dispose of the object.

Pop must watch out for the empty stack condition, but is straightforward otherwise. Since pop only discards an empty frame on under flow, the empty stack condition can come up two ways. Either there are no frames, or the topmost frame is empty. After making these two tests, pop simply returns the topmost element after decrementing the frame pointer.

Pick is quite interesting. In a normal stack implementation this would be a very simple operation. After range checking the index, a simple subtract would yield the array index of the requested element. The frame based implementation makes the pick operation much more complex in general. Also, deep picks pay a performance penalty, although shallow picks are not much worse than in a simple stack. Pick must calculate which frame in the frame list contains the required element. After that, it must calculate the element offset in the target frame. While walking back along the frame links pick must be careful not to run off the end. Since pick doesn’t know the depth of the stack at the outset, there is no way to range check before walking the list.

StackSweekObj.another also has a few kinks. Two new pieces of state are added to the generic iterator object so that another can keep track of where it is. SubjectFrame is a pointer to the current frame, and subjectPointer is the current index within subjectFrame. The large size (for a method) of another might seem a little surprising. After all, what‘s so hard about sweeping down a stack? A lot of the code tests end-case conditions. The fact that another is so large is a good argument for using iterator objects instead of main line program code. Without a predefined iterator, every client code loop would need to include this code. By using an iterator, the client code is simple, straightforward, readable, and more likely to be correct.


The acid test for any object definition is reusability. Well thought out data and method factoring aids reusability. The object design guidelines presented here aid the development of reusable objects. I hope you find them to be a good starting point as you develop your own set of guidelines and your own object library.

Listing 1

unit lists;
 ObjIntf, iterators;

 listObj = object (TObject)
 first : listElementObj;
 procedure init;
 procedure append (e : listElementObj);
 procedure appendList (l : listObj);
 procedure insert (e : listElementObj);
 procedure head (var e : univ listElementObj);
 procedure release;
 listElementObj = object (TObject)
 next : listElementObj;
 procedure init;
 function release : boolean;

 listIterator = object (iterator)
 function another 
 (var anObject : univ TObject) : boolean;

procedure listObj.init;
 first := nil end;

procedure listObj.append 
 (e : listElementObj);
 t : listElementObj;
 if first = nil
 then first := e
 else begin
 t := first;
 while <> nil do t :=; := e end end;
procedure listObj.appendList (l : listObj);
 p : listElementObj;
 append (l.first);
 l.first := nil;
 l.release end;
procedure listObj.insert
 (e : listElementObj);
begin := first;
 first := e end;

procedure listObj.head
 (var e : univ listElementObj);
 e := first;
 if first <> nil { guards both potential nil references, e and first 
 then begin
 first :=; := nil end end;

procedure listObj.release;
 t, t2 : listElementObj;
 t := first;
 while t <> nil do begin
 t2 := t;
 t :=;
 {release list element storage, if it has any}
 if t2.release 
 then dispose (t2) end;
 dispose (self) end;

procedure listElementObj.init;
 next := nil end;

{ Override this if your list element must release
 storage before being disposed of.  Return true
 if the element can be disposed of. If other
 references to the element still exist, return
 false.  This generic release assumes that only 1 
 reference to any element exist.}
function listElementObj.release : boolean;
 release := true end;
function listIterator.another 
 (var anObject : univ TObject) : boolean;
 if i = nil
 then i := listObj(subject).first
 else i := listElementObj(i).next;
 anObject := i;
 another := i <> nil end;
Listing 2

unit iterators;

 iterator = object (TObject)
 subject, { the subject data structure }
 i {the loop variable, updated by method 
 : TObject;
 procedure init (theSubject : TObject);
 procedure loopReset;
 function another
 (var anObject : univ TObject) : boolean;


{ the iterator must be associated with a data structure that will be 
the loop subject }
procedure iterator.init
 (theSubject : TObject);
 subject := theSubject;
 i := nil end;

{ generic loop reset just clears out the iteration variable }  
procedure iterator.loopReset;
 i := nil end;

{Method 'another' serves up each element of the subject data structure 
exactly once, one at a
 time.  Override this function with the following
  1) if iterator.i is nil, start a loop by setting
 i to the first operand
  2) if iterator.i is not nil, advance i to next
  3) return i in anObject
  4) return true if anObject is non-nil
  5) when there are no more operands:
   a) set i to nil (exit in reset condition)
 b) return nil in anObject
 c) return false }
function iterator.another
 (var anObject : univ TObject) : boolean;
begin end;

Listing 3

{$SETC compileForMPW := true }
{ if false, then compile for application }
{ How to use:
 1) create a memCheckObj (or a descendant) with
 new(aMemCheckObj) (this can be 'new(memCheck)'
 if you don't override any methods)
 2) initialize before making any more 
 'new(someObject)' or 'newHandle' calls
 3) loop on gotObject until new(someObject) 
 succeeds, as in:
 new (obj)
 until memCheck.gotObject (obj);

 Advanced features:
 1) override collectGarbage with application 
 dependant code to free up space (optional)
 2) override outOfMemExit to do any clean up 
 before exiting through inherited outOfMemExit
 The outOfMemAlertId parameter to initMemCheck is
 the id of an alert that will be posted before
 the program is halted.  It is ignored in MPW
 mode. }
unit memErr;
 {$IFC compileForMPW}

 memCheckObj = object (TObject) 
 memRetryCount : integer;
 procedure init;
 function gotObject (o : TObject) : boolean;
 function collectGarbage : boolean;
 procedure outOfMemExit;
procedure initMemCheck
 (mc : memCheckObj; outOfMemAlertId : integer);

 memCheck : memCheckObj;
 { reference this object in your code }


 theAlertId : integer;

procedure memCheckObj.init;
 memRetryCount := 0 end;

function memCheckObj.gotObject
 (o : TObject) : boolean;
 if o <> nil { was newHandle successful? }
 then begin
 gotObject := true;
 memRetryCount := 0 end
 else begin
 gotObject := false;
 if collectGarbage { could we make any space? }
 then memRetryCount := succ (memRetryCount)
 else outOfMemExit end end;

{ If your data structures can be garbage 
 collected, override this method.
 1) return true if any space was made
 2) use memRetryCount to detect multiple
 collectGarbage calls for the same 'new()'.
 memRetryCount = 0 on first call. }
function memCheckObj.collectGarbage :
 collectGarbage := false end;

{ override with clean-up routine, if necessary }
procedure memCheckObj.outOfMemExit;
{$IFC compileForMPW}
 writeln (diagnostic, 'out of memory');
 halt end;
 dummy : integer;
 dummy := StopAlert (theAlertId);
 halt end;

procedure initMemCheck
 (mc : memCheckObj; outOfMemAlertId : integer);
 if mc <> nil
 then begin
 memCheck := mc;
 theAlertId := outOfMemAlertId;
 {$IFC not compileForMPW}
 { do this now, while we have the memory! }
 memCheck.init end
 else halt end;


program testLists (input, output);
 ObjIntf, iterators, lists, memCheck, PasLibIntf;
 testListObj = object (listObj)
 procedure dump;
 testListElementObj = object (listElementObj)
 data : integer;
 procedure dump;
 { some lists to test with }
 testList, testList2 : testListObj;
 { a temporary }
 t : testListElementObj;

procedure testListObj.dump;
 dumping : listIterator;
 dumperand : testListElementObj;
 repeat new (dumping)
 until memCheck.gotObject(dumping);
 writeln (output, 'Dump:');
 while dumping.another(dumperand) do
 writeln (output) end;

procedure testListElementObj.dump;
 writeln (output, data) end;

begin { Main program }
 PLSetHeapType(true); { allow dispose }
 { start up the allocation checker }
 new (memCheck);
 initMemCheck (memCheck, 0);
 repeat new (testList)
 until memCheck.gotObject(testList);
 write (output, 'Should be empty -- '); 
 repeat new (t)
 until memCheck.gotObject(t);
 t.init; := 1;
 write (output, 'One element(1) -- '); 

 repeat new (t)
 until memCheck.gotObject(t);
 t.init; := 2;
 write (output, 'Two elements(2 1) -- '); 
 repeat new (t)
 until memCheck.gotObject(t);
 t.init; := 3;
 write (output, 'Three elements(2 1 3) -- '); 
 write (output, 'Two elements(1 3) -- '); 
 repeat new (testList2)
 until memCheck.gotObject(testList2); 
 testList2.append(t); {append to an empty list}
 write (output, 'List 2, one element (2) -- '); 
 {disposes testList2 }  testList.appendList (testList2); 
 write (output, 'Three elements (1 3 2) -- ');
Listing 4

unit stacks;
 ObjIntf, iterators, lists, memCheck;

 stackFrameDepth = 15; {(cells per frame) - 1}
 stackFrameFull = stackFrameDepth + 1;
 stackObj = object (listObj)
 procedure push (o : TObject);
 procedure pop (var o : univ TObject);
 procedure pick 
 (n : integer; var o : univ TObject);
 function okToPush (o : TObject) : boolean;
 procedure rejectPush (o : TObject);

 framePointerType = 0..stackFrameFull;
 stackFrameObj = object (listElementObj)
 framePointer : framePointerType; 
 { points to next empty cell }
 cell : array [0..stackFrameDepth] of TObject;
 procedure init; override;
 stackSweepObj = object (iterator)
 subjectFrame : stackFrameObj;
 subjectPointer : framePointerType;
 function another
 (var anObject : univ TObject) : boolean; 

{ A stack is stored in a list of stack frames.
 push and pop only add/delete a frame when
 absolutely necessary, in order to avoid
 duplicating work when successive push/pop or
 pop/push operations occur at stack frame
 boundaries. }

procedure stackObj.push (o : TObject);
 procedure addAFrame;
 f : stackFrameObj;
 new (f)
 until memCheck.gotObject (f);
 self.insert (f) end;

 if okToPush (o)
 then begin
 { don't combine these next two tests! 
 MUST guard second test with first! }
 if first = nil
 then addAFrame; { stack is empty }
 if stackFrameObj(first).framePointer 
 = stackFrameFull
 then addAFrame; { last frame is full -- start
 a new one }
 [stackFrameObj(first).framePointer] := o;
 := succ (stackFrameObj(first).framePointer)
 else rejectPush (o) end;

procedure stackObj.pop (var o : univ TObject);
 f : stackFrameObj;
 if first = nil
 then { stack is empty }
 o := nil
 else begin
 if stackFrameObj(first).framePointer = 0
 then begin { roll back a frame }
 if f.release then dispose (f) end;
 if first = nil
 then { stack is empty }
 o := nil
 else begin { pluck out an object }
  := pred(stackFrameObj(first).framePointer);
 o := stackFrameObj(first).cell
 end end end;

{ Pick 0 = top of stack, nil returned in o if we
 run off the end. Pick makes no modification to
 the stack's contents. }
procedure stackObj.pick
 (n : integer; var o : univ TObject);
 frameNum : integer; { counts down to frame that
 contains the cell we want }
 frame : stackFrameObj;
 if first = nil
 o := nil
 else begin
 if n < stackFrameObj(first).framePointer
 then { cell is in first frame }
 o := stackFrameObj(first).cell
 [stackFrameObj(first).framePointer - (n+1)]
 else begin { chase links to correct frame; start with second frame }
 frameNum :=
 ((n - stackFrameObj(first).framePointer)
 div stackFrameFull) - 1;
 frame := stackFrameObj(;
 while frameNum > 0 do begin
 if frame <> nil { did we go off the end? }
 then frame := stackFrameObj(
 else frameNum := 0; { off end - bail out }
 frameNum := pred(frameNum) end;
 if frame <> nil
 o := stackFrameObj(frame).cell
 [stackFrameDepth - 
 ((n - stackFrameObj(first).framePointer)
 mod stackFrameFull)]
 else o := nil end end end;

{ default screen passes anything }
function stackObj.okToPush 
 (o : TObject) : boolean;
 okToPush := true end;

{ default exception trap is an ostrich -- it
 ignores the problem. }
procedure stackObj.rejectPush (o : TObject);
begin end;

procedure stackFrameObj.init; override;
 inherited init;
 framePointer := 0 end;
function stackSweepObj.another
 (var anObject : univ TObject) : boolean;
 { test for loop reset condition }
 if i = nil
 then begin
 { start at the top }
 := stackFrameObj(stackObj(subject).first);
 if subjectFrame <> nil
 := subjectFrame.framePointer end;

 { test for end of frame condition }
 if subjectFrame <> nil
 then begin
 if subjectPointer = 0
 then begin
 { access next frame }
 := stackFrameObj(;
 if subjectFrame <> nil
 subjectPointer := subjectFrame.framePointer
 end end;

 { return an element }
 if subjectFrame <> nil
 then begin
 subjectPointer := pred (subjectPointer);
 i := subjectFrame.cell[subjectPointer] end
 { stack is empty }
 i := nil;
 { exit }
 anObject := i;
 another := anObject <> nil end;



Community Search:
MacTech Search:

Software Updates via MacUpdate

Civilization VI 1.1.0 - Next iteration o...
Sid Meier’s Civilization VI is the next entry in the popular Civilization franchise. Originally created by legendary game designer Sid Meier, Civilization is a strategy game in which you attempt to... Read more
Network Radar 2.3.3 - $17.99
Network Radar is an advanced network scanning and managing tool. Featuring an easy-to-use and streamlined design, the all-new Network Radar 2 has been engineered from the ground up as a modern Mac... Read more
Quicken 5.5.6 - Complete personal financ...
Quicken makes managing your money easier than ever. Whether paying bills, upgrading from Windows, enjoying more reliable downloads, or getting expert product help, Quicken's new and improved features... Read more
Civilization VI 1.1.0 - Next iteration o...
Sid Meier’s Civilization VI is the next entry in the popular Civilization franchise. Originally created by legendary game designer Sid Meier, Civilization is a strategy game in which you attempt to... Read more
Network Radar 2.3.3 - $17.99
Network Radar is an advanced network scanning and managing tool. Featuring an easy-to-use and streamlined design, the all-new Network Radar 2 has been engineered from the ground up as a modern Mac... Read more
Printopia 3.0.8 - Share Mac printers wit...
Run Printopia on your Mac to share its printers to any capable iPhone, iPad, or iPod Touch. Printopia will also add virtual printers, allowing you to save print-outs to your Mac and send to apps.... Read more
ForkLift 3.2.1 - Powerful file manager:...
ForkLift is a powerful file manager and ferociously fast FTP client clothed in a clean and versatile UI that offers the combination of absolute simplicity and raw power expected from a well-executed... Read more
BetterTouchTool 2.417 - Customize multi-...
BetterTouchTool adds many new, fully customizable gestures to the Magic Mouse, Multi-Touch MacBook trackpad, and Magic Trackpad. These gestures are customizable: Magic Mouse: Pinch in / out (zoom... Read more
Little Snitch 4.0.6 - Alerts you about o...
Little Snitch gives you control over your private outgoing data. Track background activity As soon as your computer connects to the Internet, applications often have permission to send any... Read more
Google Chrome 65.0.3325.181 - Modern and...
Google Chrome is a Web browser by Google, created to be a modern platform for Web pages and applications. It utilizes very fast loading of Web pages and has a V8 engine, which is a custom built... Read more

Latest Forum Discussions

See All

The best games that came out for iPhone...
It's not a huge surprise that there's not a massive influx of new, must-buy games on the App Store this week. After all, GDC is happening, so everyone's busy at parties and networking and dying from a sinister form of jetlag. That said, there are... | Read more »
Destiny meets its mobile match - Everyth...
Shadowgun Legends is the latest game in the Shadowgun series, and it's taking the franchise in some interesting new directions. Which is good news. The even better news is that it's coming out tomorrow, so if you didn't make it into the beta you... | Read more »
How PUBG, Fortnite, and the battle royal...
The history of the battle royale genre isn't a long one. While the nascent parts of the experience have existed ever since players first started killing one another online, it's really only in the past six years that the genre has coalesced into... | Read more »
Around the Empire: What have you missed...
Oh hi nice reader, and thanks for popping in to check out our weekly round-up of all the stuff that you might have missed across the Steel Media network. Yeah, that's right, it's a big ol' network. Obviously 148Apps is the best, but there are some... | Read more »
All the best games on sale for iPhone an...
It might not have been the greatest week for new releases on the App Store, but don't let that get you down, because there are some truly incredible games on sale for iPhone and iPad right now. Seriously, you could buy anything on this list and I... | Read more »
Everything You Need to Know About The Fo...
In just over a week, Epic Games has made a flurry of announcements. First, they revealed that Fortnite—their ultra-popular PUBG competitor—is coming to mobile. This was followed by brief sign-up period for interested beta testers before sending out... | Read more »
The best games that came out for iPhone...
It's not been the best week for games on the App Store. There are a few decent ones here and there, but nothing that's really going to make you throw down what you're doing and run to the nearest WiFi hotspot in order to download it. That's not to... | Read more »
Death Coming (Games)
Death Coming Device: iOS Universal Category: Games Price: $1.99, Version: (iTunes) Description: --- Background Story ---You Died. Pure and simple, but death was not the end. You have become an agent of Death: a... | Read more »
Hints, tips, and tricks for Empires and...
Empires and Puzzles is a slick match-stuff RPG that mixes in a bunch of city-building aspects to keep things fresh. And it's currently the Game of the Day over on the App Store. So, if you're picking it up for the first time today, we thought it'd... | Read more »
What You Need to Know About Sam Barlow’s...
Sam Barlow’s follow up to Her Story is #WarGames, an interactive video series that reimagines the 1983 film WarGames in a more present day context. It’s not exactly a game, but it’s definitely still interesting. Here are the top things you should... | Read more »

Price Scanner via

Thursday roundup of the best 13″ MacBook Pro...
B&H Photo has new 2017 13″ MacBook Pros on sale for up to $200 off MSRP. Shipping is free, and B&H charges sales tax for NY & NJ residents only. Their prices are the lowest available for... Read more
Sale: 9.7-inch 2017 WiFi iPads starting at $2...
B&H Photo has 9.7″ 2017 WiFi Apple iPads on sale for $40 off MSRP for a limited time. Shipping is free, and pay sales tax in NY & NJ only: – 32GB iPad WiFi: $289, $40 off – 128GB iPad WiFi: $... Read more
Roundup of Certified Refurbished iPads, iPad...
Apple has Certified Refurbished 9.7″ WiFi iPads available for $50-$80 off the cost of new models. An Apple one-year warranty is included with each iPad, and shipping is free: – 9″ 32GB WiFi iPad: $... Read more
Back in stock! Apple’s full line of Certified...
Save $300-$300 on the purchase of a 2017 13″ MacBook Pro today with Certified Refurbished models at Apple. Apple’s refurbished prices are the lowest available for each model from any reseller. A... Read more
Wednesday deals: Huge sale on Apple 15″ MacBo...
Adorama has new 2017 15″ MacBook Pros on sale for $250-$300 off MSRP. Shipping is free, and Adorama charges sales tax in NJ and NY only: – 15″ 2.8GHz Touch Bar MacBook Pro Space Gray (MPTR2LL/A): $... Read more
Apple offers Certified Refurbished Series 3 A...
Apple has Certified Refurbished Series 3 Apple Watch GPS models available for $50, or 13%, off the cost of new models. Apple’s standard 1-year warranty is included, and shipping is free. Numerous... Read more
12″ 1.2GHz Space Gray MacBook on sale for $11...
B&H Photo has the Space Gray 12″ 1.2GHz MacBook on sale for $100 off MSRP. Shipping is free, and B&H charges sales tax for NY & NJ residents only: – 12″ 1.2GHz Space Gray MacBook: $1199 $... Read more
Mac minis available for up to $150 off MSRP w...
Apple has restocked Certified Refurbished Mac minis starting at $419. Apple’s one-year warranty is included with each mini, and shipping is free: – 1.4GHz Mac mini: $419 $80 off MSRP – 2.6GHz Mac... Read more
Back in stock: 13-inch 2.5GHz MacBook Pro (Ce...
Apple has Certified Refurbished 13″ 2.5GHz MacBook Pros (MD101LL/A) available for $829, or $270 off original MSRP. Apple’s one-year warranty is standard, and shipping is free: – 13″ 2.5GHz MacBook... Read more
Apple restocks Certified Refurbished 2017 13″...
Apple has Certified Refurbished 2017 13″ MacBook Airs available starting at $849. An Apple one-year warranty is included with each MacBook, and shipping is free: – 13″ 1.8GHz/8GB/128GB MacBook Air (... Read more

Jobs Board

Payments Counsel - *Apple* Pay (payments, c...
# Payments Counsel - Apple Pay (payments, credit/debit) Job Number: 112941729 Santa Clara Valley, California, United States Posted: 26-Feb-2018 Weekly Hours: 40.00 Read more
Firmware Engineer - *Apple* Accessories - A...
# Firmware Engineer - Apple Accessories Job Number: 113452350 Santa Clara Valley, California, United States Posted: 28-Feb-2018 Weekly Hours: 40.00 **Job Summary** Read more
*Apple* Solutions Consultant - Apple (United...
# Apple Solutions Consultant Job Number: 113501424 Norman, Oklahoma, United States Posted: 15-Feb-2018 Weekly Hours: 40.00 **Job Summary** Are you passionate about Read more
*Apple* Inc. Is Look For *Apple* Genius Te...
Apple Inc. Is Look For Apple Genius Technical Customer Service Minneapolis Mn In Minneapolis - Apple , Inc. Apple Genius Technical Customer Service Read more
*Apple* Genius Technical Customer Service Co...
Apple Genius Technical Customer Service Columbus Oh Apple Inc. - Apple , Inc. Apple Genius Technical Customer Service Columbus Oh - Apple , Inc. Job Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.