TweetFollow Us on Twitter

OD Dynamic Updating
Volume Number:12
Issue Number:8
Column Tag:Opendoc

Updating Parts Dynamically With SOM

Fast, automatic inter-part communication

By Jeremy Roschelle

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

As a developer of learning technologies, I am concerned with integrated tools for math and science education. Learners need a wide variety of views, such as meters, graphs, tables, calculators, simulations, data analyzers. Ideally the same object (an accelerated particle, say) can be simultaneously updated in every view.

In the past, all these visualizations would have to be built as a parts of a single monolithic application - a huge effort. With OpenDoc, my colleagues and I are building each view as a separate OpenDoc component. Students can simply drag and drop a particle from a simulation to a graph to a meter. For example, Figure 1 shows a simulation of an oscillating ball, with a vector meter (written by Arni McKinley of Minds in Motion), an adjustable equation (which I wrote), a text editor part, and a graph (by Jim Correia).

The simulation, equation, graph, and vector meter are each separate components, written and compiled at different times, and with different frameworks (PowerPlant, OpenDoc Development Framework, and SamplePart). Yet, as the particle moves, all views instantly update to show its current position, velocity, and acceleration. Furthermore, new views can be added to the system by any developer. For example, Steve Beardslee at Tufts University has written a data collection component that drives a particle with real data from a cart moving on a frictionless track.

Figure 1. A component-based physics lesson

The ability to mix and match components should lead to a rapidly expanding and improving collection of OpenDoc components for math and science education. This article is your opportunity to help education and learn something about OpenDoc at the same time. By the end, you’ll know everything you need to contribute a particle viewer to the suite. In fact, you’ll build a digital meter that drops right into a window and interoperates with the simulation. I guarantee you’ll have a warm, satisfied feeling when you see your very own component working alongside the ones that Arni, Steve, Jim, and I wrote.

The techniques described in this article also apply to many areas besides education. In fact, they will be useful in any application where one kind of data object is displayed in many different views. Examples would be a personal information manager or a financial checkbook program. If you implement the techniques here, OpenDoc could offer your customers some powerful solutions. They could freely mix and match various viewers to suit their needs. You could sell specific advanced editors to specific niche markets. Fast, object-based data sharing could let your company offer a highly integrated solution with many option packages and upgrade potentials.

SOM: A Better Way to Share Data

Using the standard OpenDoc APIs, editors and viewers share data by writing out a stream format either to the clipboard or to a link storage unit. This is similar to the familiar techniques for handling the clipboard, or the System 7 publish-and-subscribe mechanism. An obvious problem with stream-based sharing is speed. The delay is particularly painful if the object is large and the client only needs a small portion of the data. A less obvious problem is that you need to standardize a stream format, which can be hard for objects with complex data. Finally, you can only change the data at the source.

A better way to share data is to share an object. Getting data is done with a method call. For example, I can get the position vector of my particle as follows:

SDataVector position(3);

The chief advantage of getting data via a method call is speed. A particle might have thousands of data points. Using this method, I get only the data point I need. But there are other advantages as well. Any viewer that has a particle can call its methods to change its data. Furthermore, you can use class inheritance to manage the growing complexity of your objects. For example, my base particle only has data access methods. My sampled particle builds upon this by introducing methods for entering sampled data. My constant acceleration particle adds specific methods for constant acceleration situations. Rather than define one format that meets every need, I can introduce classes that inherit from the core and specialize appropriately for particular situations. Due to the magic of object-oriented programming, older viewers will continue to work with newer, more advanced classes (whereas older viewers would typically crash with newer file formats).

OpenDoc makes it possible to share objects among parts by using the System Object Model (SOM). SOM packages an object binary in a special shared library, where it can be used from many OpenDoc editors, regardless of the compiler or language used to code the editor. Among the numerous advantages of SOM, it solves the fragile base class problem; you can change an object’s interface methods, and older editors will continue to work without your needing to recompile them. While in-depth coverage of SOM is not possible here, you will get a strong feeling for its ease of use and power. Full reference materials for SOM are on the OpenDoc Developer CD and at the OpenDoc Web site.

Becoming a SOM User

Soma: an intoxicating plant juice referred to
in the literature of Vedic ritual.

- Webster’s New Collegiate Dictionary, 1972, p. 1356

Unlike soma, SOM is not a drug. But I’ll admit that SOM’s ability to make component software flow is somewhat intoxicating, even if it does require a few arcane rituals.

To use a SOM-based class, you first include its header, which has the .xh suffix. This is just like including the .h header for a C++ class. You call the methods of the class, including new and delete, just as you would for an ordinary C++ class. You must also include the SOM shared library in your project.

The example below uses the particle class to calculate the position of a ball that is thrown in the air for 10 seconds.

Calculate Position
#include "EduConstAccParticle.xh"

double CalculatePosition(Environment *ev)
 double finalPos = 0.0;
 EduConstAccParticle *p = nil;
 p = new EduConstAccParticle;
    // SDataVector is a C++ utility class that makes it easy
    // to manipulate vectors. 1 is the number of dimensions.
    // SDataVector uses 1-based indexing, because I have trouble thinking
    // of the x-axis as the zeroeth dimension of a 3D vector.
 SDataVector pos(1), vel(1), acc(1);
 pos[1] = 0.0;
 vel[1] = 300.0;
 acc[1] = -9.8;
    // set to constant acceleration motion, with 0.0 initial position and 300.0 m/s
    // and gravitational acceleration.

    // get the value at 10 seconds
 finalPos = p->Get1Position(ev,10.0,1);
 delete p;
 delete p;
 return finalPos;

One difference you will note in using SOM is the first argument, which is always an environment pointer. The environment is a struct that SOM uses to return exceptions. The environment is necessary because a SOM object may not be in the same address space as its caller, and thus may not be able to throw an ordinary exception. Upon returning from SOM code you should always check the environment for errors. To make this easy, most SOM headers are produced with an option (CHECK-ENV) that automatically converts the environment into a C++ exception if it contains an error. Thus your code can catch SOM-based exceptions and C++-based exceptions with the same try-catch blocks. You should always have at least one exception handler active at the time you call a SOM method (lest you abort your process upon the first bad environment that is returned).

Since you now know how to call SOM methods - it’s not very different from C++ - we can move to the real issue: how can particle classes be designed to allow multiple live views?

Sharing Particles: A Class Act

My goal is to enable multiple components, like simulations, graphs, and tables, to simultaneously display the data of a single object. To accomplish this, I use the model-view-controller strategy (Figure 2), made famous in SmallTalk. In this strategy, the controller (a simulation) makes changes to the model (a particle), and the particle notifies all its views (graphs, meters, tables). The views call the particle’s methods to fetch the appropriate data for updating the display.

Figure 2. Model-View-Controller Architecture

Three classes are needed to implement the strategy in OpenDoc:

1. EduObject: a sharable object. (EduParticle inherits from EduObject.)

2. EduObjectListener: registers for change notification with a particular EduObject.

3. EduObjectMgr: publishes EduObjects to other components.

Each display will have a pointer to an EduObject. A meter, for example, gets a pointer to an EduParticle by receiving a reference in a drag. The method EduObjectListener::GetObject uses the particle reference to get access to the EduParticle, and the meter uses this EduParticle to get data it can display.

The simulation changes a particle by calling its methods. For example, SetCurrentTime changes the simulation time for the particle. This method will also broadcast a message to every EduObjectListener. When a meter receives the message in its listener, for example, it will call EduParticle methods to extract data and update its display. Every time the simulation changes a particle, all the listeners will be notified and will update their displays.

Finally, it is desirable for the meter to remember its particle when the document is saved, so that the link between the simulation and the meter will persist when the document is opened again. Since the particle is a pointer, it cannot be saved directly. Instead, the meter’s listener writes out a reference to the particle (in exactly the same format used to receive a drag). When it is opened, the meter can get the particle object using the GetObject method as described above.

The listings below are an abridged version of the interface definitions which specify the API to the classes. These are written in SOM’s Interface Definition Language (IDL). IDL reads almost like a C++ class definition, except that each argument is explicitly marked for direction of data flow: in, out, or inout.

EduObject Interface
interface EduObject : SOMObject
    // abridged interface definition

    // change notification methods
 void RegisterListener(in EduObjectListener listener);
 void UnRegisterListener(in EduObjectListener listener);
 void TellListeners(in long message, inout ODByteArray param);
    // support for dragging: externalizes a reference to this object.
    // storage should be the drag storage unit,
    // EduObjectListener has a method for internalizing this from storage
 void ExternalizeReference(in ODStorageUnit storage);

EduParticle Interface
// inherits from EduObject
interface EduParticle : EduObject
    // abridged interface definition

    // inherits all EduObject methods
    // EduConstAccParticle and EduSampledPosParticle 
    // add methods for setting data values
    // access to data values
 void GetPosition(in double time, inout EduDataVector vector);
 void GetVelocity(in double time, inout EduDataVector vector);
 void GetAcceleration(in double time, inout EduDataVector vector);
    // time related information
 double GetCurrentTime();
 void SetCurrentTime(in double time);

EduObjectListener Interface
interface EduObjectListener : SOMObject
    // abridged interface definition

    // messages are received here
 void ListenToMessage(in long message, inout ODByteArray param);
    // support for receiving drags
    // call HasValidObjectReference to see if you should accept this drag
    // call InternalizeReference to receive the drag
 ODBoolean HasValidObjectReference(
 in ODStorageUnit dragStorage);
 void InternalizeReference(in ODStorageUnit dragStorage);

    // utility methods for storing and retrieving a particle reference
    // in this components storage unit (the OpenDoc generalization of a file)
 void Externalize(
 in ODStorageUnit su, 
 in ODDraftKey key, 
 in ODFrame scopeFrame);

 void Internalize(in ODStorageUnit su);
    // get the object associated with this listener
    // may return nil if no object is connected.
 EduObject GetObject();

EduObjectMgr Interface
interface EduObjectMgr : ODExtension
    // abridged interface definition
    // called from another component to get an object
 EduObject GetObject(in long id);
    // called from within this component to publish objects
 void AddObject(in EduObject object);
 void RemoveObject(in EduObject object);

This article takes advantage of the encapsulation offered in these classes by separating the details of use from implementation. I’ll start with how to use the classes, since that is really easy. At the end, I’ll draw back the curtain and reveal how it works.

Building a DigitalMeter Component

The simplest way to make an OpenDoc component is with PartMaker (available at PartMaker will generate a complete CodeWarrior project, ready to compile and run. We’ll start with SamplePart, a very simple “Hello, World!” type component, and modify it to be a digital meter for a particle. To begin, drop SamplePart C++ onto PartMaker. Name your project “DigitalMeter”. You can use whatever company name you like. When PartMaker finishes grinding away, you will have a new folder named DigitalMeter.

Open the project DigitalMeter.µ, and perform a make. This will produce a new OpenDoc component. Make an alias to this component and put the alias in your Editors folder. Then drop the component onto the OpenDoc icon (found in System Folder:Extensions:OpenDoc Libraries:) to make stationery. The stationery will appear in your Stationery Folder. You should be able to double-click this stationery in order to open your brand new component. Yeah!

Now you should download EduObject and EMSim from my Web site, using the EMDemo link ( Drop the EduObject library, and EMSim and EMMeter parts, into your Editors folder. You will also need ODFDraw or KickStart Draw, as I use these as a generic container. You can get them from the OpenDoc CD or Web site. To see if you have everything installed correctly, try opening the Example document. You should see the simulation and my existing meter. Select the simulation, and choose Go from its menu. The simulation will run and the meter will update.

Drag your Digital Meter stationery into the Example document. Your component will be added to the window. Now all we need to do is to make your DigitalMeter act like it really is a meter. There are five steps involved:

1. Creating a particle listener

2. Displaying particle data

3. Receiving a particle drag

4. Listening to particle changes

5. Saving a particle reference

1. Creating a particle listener

To begin, add the EduObject library to your project (it is a shared library so it won’t add its code into your component). Then add the SDataVector.cp file to your project.

The code below creates an EduObjectListener in your DigitalMeter. You need a listener because it is both your handle to the EduParticle (which is created by the simulation part) and your way to receive messages when the particle changes. As you can see, creating a SOM object is no big deal.


void DigitalMeter::Initialize( Environment* ev )
 fListener = new EduObjectListener;
 ev,fSelf,ListenToMessage,(unsigned long)this);
 CopyPascalString(fCurrentValue,"\pDrag a particle here");
    // unchanged code below here, omitted for clarity


void DigitalMeter::ReleaseAll( Environment* ev )
 delete fListener;
    // unchanged code below here, omitted for clarity

Below I’ve listed the additions to the DigitalMeter header for all the code in this article:

// original top of header omitted for clarity
// my changes below
#include <EduObjectListener.xh> /
#include "ListenerHelper.h"
#include <EduParticle.xh> //+
#include "SDataVector.h"
#include <DgItmIt.xh>
#include <FacetItr.xh>
#include <stdio.h>

class DigitalMeter {
    // new methods
 static void ListenToMessage(
 Environment *ev, unsigned long inRefCon, 
 long inMessage, ODByteArray *ioParam);
 Environment *ev, ODDragItemIterator *dragInfo);
 void   ReceiveDrop(
 Environment *ev, ODDragItemIterator *dragInfo);
 void   UpdateDisplay(Environment *ev);
 void   DrawAll(Environment *ev);
    // new members
 EduObjectListener *fListener;
 Str255 fCurrentValue;
    // unchanged class definition below here, omitted for clarity

2. Digital display

The digital display just draws a string containing the position of the particle. It gets the particle from the listener. (You will see how the listener gets the particle in the next section.) The string is stored in the fCurrentValue member, which will be set by the UpdateDisplay method. Once the string is built, the UpdateDisplay method calls DrawAll, which redraws every facet of every frame with the new string.


void DigitalMeter::UpdateDisplay(Environment *ev)
 EduParticle *particle = (EduParticle *)
 if (! particle) return;
    // get current position
 double time = particle->GetCurrentTime(ev);
    // use ANSI routines to write numbers into Pascal string
 char *output = (char *)&fCurrentValue[1];
 sprintf(output, "%.1f,%.1f",pos[1],pos[2]);
    // set length of Pascal string
 fCurrentValue[0] = strlen(output);

void DigitalMeter::DrawFrameView(
 Environment* ev,ODFacet* facet )
    // setup text characteristics
 FontInfo finfo;
 MoveTo(0,finfo.ascent + 2);

void DigitalMeter::DrawAll(Environment *ev)
    // iterate through frames, then facets
 CListIterator fiter(fDisplayFrames);
 for ( CFrameProxy* proxy = (CFrameProxy*) fiter.First();
 proxy = (CFrameProxy*) fiter.Next() )
    // draw only frames in RAM, not any still on disk
 if (proxy->FrameIsLoaded(ev)) {
 ODFrame*frame = proxy->GetFrame(ev);
    // iterate through facets
 ODFrameFacetIterator *iter =
 for (ODFacet *facet = iter->First(ev);
  facet = iter->Next(ev)) {
 delete iter;

3. Its a drag!

The DigitalMeter’s listener initially learns about a particle by receiving a dragged particle reference. To receive any drag, each frame must have its flag set correctly. We set the flag when your frame is added or connected:


void DigitalMeter::DisplayFrameAdded(
 Environment* ev, ODFrame* frame )
    // unchanged code below here, omitted for clarity


void DigitalMeter:: DisplayFrameConnected (
  Environment* ev, ODFrame* frame )
    // unchanged code below here, omitted for clarity

The basic principle of receiving a dragged particle is simple. When something is dragged into your frame, OpenDoc will call DragEnter and pass in a storage unit that contains the dragged content. If your part decides the drag is acceptable, then when the user releases the drag, your receive drop method will be called. EduObjectListener has two methods for receiving drags. HasValidObjectReference returns true if the listener can accept the drag, and InternalizeObjectReference actually handles the drop.

ODBoolean DigitalMeter::IsAcceptable(
 Environment *ev, ODDragItemIterator *dragInfo)
 ODStorageUnit *sue = dragInfo->First(ev);
 return fListener->HasValidObjectReference(ev,sue);

void    DigitalMeter::ReceiveDrop(
 Environment *ev, ODDragItemIterator *dragInfo)
 ODStorageUnit *sue = dragInfo->First(ev);

Unfortunately, SamplePart has only stub methods for receiving drops, so we have to delve into the SOM class implementation of the part. The code below looks funny, but is basically just a C++ function implementation with a little extra work to interface with SOM. As with all methods in the SOM class implementation, this code just sets up exception handling and then delegates the work to the DigitalMeter class. For simplicity’s sake, drag hiliting is not implemented here.


SOM_Scope ODDragResult SOMLINK som_DigitalMeter__DragEnter(
 PartCo_som_DigitalMeter *somSelf, Environment *ev,
 ODDragItemIterator* dragInfo, ODFacet* facet,
 ODPoint* where)
 PartCo_som_DigitalMeterData *somThis = 
 ODBooleanresult = kODFalse;
 result = _fPart->IsAcceptable(ev,dragInfo);
 return result;

SOM_Scope ODDragResult  SOMLINK som_DigitalMeter__DragWithin(
 PartCo_som_DigitalMeter *somSelf, Environment *ev,
 ODDragItemIterator* dragInfo, ODFacet* facet,
 ODPoint* where)
    // same as DragEnter
 return somSelf->DragEnter(ev,dragInfo,facet,where);

SOM_Scope ODDropResult  SOMLINK som_DigitalMeter__Drop(
 PartCo_som_DigitalMeter *somSelf, Environment *ev,
 ODDragItemIterator* dropInfo, ODFacet* facet,
 ODPoint* where)
 PartCo_som_DigitalMeterData *somThis = 
 return kODDropCopy;

4. Listening in

Now we get to the easy part. Every time the particle changes, your DigitalMeter will get a message. The ListenToMessage method simply calls UpdateDisplay to display the new values

 Environment *ev, unsigned long refCon,
 long inMessage, ODByteArray *ioParam)
 DigitalMeter  *me = (DigitalMeter *)refCon;
 switch (inMessage) {
 case EduParticle_msgDataChanged:
 case EduParticle_msgTimeChanged:

5. Being persistent

As a final touch, it’s nice to store the particle reference in your part’s content, so that the same particle can be displayed in the meter when the document is opened the next time. EduObjectListener handles the work of doing this for you.

 Environment* ev, ODStorageUnit* storageUnit )
 if (ODSUExistsThenFocus(ev,storageUnit, 
 kODPropContents,kDigitalMeterKind )) {

 Environment* ev, ODStorageUnit*   storageUnit,
 ODDraftKey key, ODFrame* scopeFrame)
 ODSUForceFocus(ev, storageUnit,
 fListener->Externalize(ev,storageUnit,key, scopeFrame);

After entering these changes, make your part and reopen the Example document. Try to drag a particle into your digital meter. You should begin seeing live updates!

How It Works: A Look Behind the Scenes

You might by now be wondering how EduObject and its related classes do their work. The key magic is really provided by SOM. Each EduObject class implementation is compiled into the EduObject library. Each component dynamically links to the library at run time. This enables each component to share the same EduObject code for particles and listeners.

To implement the classes, I first wrote the interface definitions (IDL files). I then submitted these to the SOM compiler, which emits .xh, .xih, and .cpp files. The .cpp file has method stubs, which I filled in with C++ implementations of each method. I compiled these into the shared library and exported each SOM class data structure.

If you want to explore the class implementations in depth, you can read the full sources, which you received when you downloaded EduObject from my Web page. Here I can only provide a quick tour of some of the key concepts behind dragging, persistence, and change notification.

To perform a drag in OpenDoc, you write data into a storage unit that represents the content of the drag. The storage unit can contain multiple flavors of data, each in a separate value. EduObject has an ExternalizeReference method which writes down two pieces of information, the ID of its containing part and the ID of the particle within the part.


SOM_Scope void  SOMLINK EduObjectExternalizeReference(
 EduObject *somSelf, Environment *ev,
 ODStorageUnit* storage)
 EduObjectData *somThis = EduObjectGetData(somSelf);
    // write partID
 ODPart *part = (ODPart *)_fMgr->GetBase(ev);
 long partID = part->GetID(ev);
 StorageUnitSetValue(storage,ev,sizeof(long), &partID);
    // write objectID
 long objectID = somSelf->GetID(ev);
 StorageUnitSetValue(storage,ev,sizeof(long), &objectID);

To receive the drag, EduObjectListener:: HasValidObjectReference just checks to see that the correct value type (flavor) is available in the drag. EduObjectListener::InternalizeObjectReference retrieves the part ID and the object ID. (Persistence basically works the same way: Externalize writes down a part ID and object ID, and Internalize reads these numbers back in.)

SOM_Scope ODBoolean  SOMLINK EduObjectListenerHasValidObjectReference(
 EduObjectListener *somSelf, Environment *ev,
 ODStorageUnit* dragStorage)
 EduObjectListenerData *somThis =  
 ODBooleanresult = kODFalse;

 result = dragStorage->Exists(ev,
 return result;

SOM_Scope void  SOMLINK EduObjectListenerInternalizeReference(
 EduObjectListener *somSelf, Environment *ev, 
 ODStorageUnit* dragStorage)
 EduObjectListenerData *somThis = 

 if (ODSUExistsThenFocus(ev,dragStorage,
 kODPropContents,EduObject_ReferenceKind)) {

Now how do we actually get a particle from the simulation to the meter? There is no way to directly get a particle (or any other object) out of an OpenDoc part; the ODPart interface simply does not have a method for returning particles! Therefore it is necessary to extend ODPart with an ODExtension. EduObjectMgr is an extension that adds a specific method for returning the object with a specific ID. This extension effectively publishes a set of particles so that other components can see them.

Within your meter, EduObjectListener::GetObject works as follows: First it uses the part ID to retrieve the ODPart that contains the particle. Then it gets the EduObjectMgr extension from that part. Finally, it asks the extension to return the particle with the specified object ID.


SOM_Scope EduObject*  SOMLINK EduObjectListenerGetObject(
 EduObjectListener *somSelf, Environment *ev)
 EduObjectListenerData *somThis = 
    // return immediately if it is in the cache
 if (_fObject) return _fObject;

 ODPart *part = kODNULL;
 EduObjectMgr *ex = kODNULL;
    // To get the particle, we first get its owner part
 part = _fThisPart->GetStorageUnit(ev)->
 ODISOStr extName = EduObjectMgr_ExtensionName;
 if (part!= kODNULL && part->HasExtension(ev,extName)) {
    // now get the extension of the part, and use it to get the object
 ex = (EduObjectMgr*)part->AcquireExtension(ev,extName);
 _fObject = ex->GetObject(ev,_fObjectID);
 ex = kODNULL;
 part = kODNULL; 
    // register to receive change notification
 if (_fObject)

 if (part) part->Release(ev);
 if (ex) ex->Release(ev);
 return _fObject;

When a EduObjectListener has an EduObject, it calls EduObject::RegisterListener. The EduObject simply stores all registered listeners in a list. EduObject:: TellListeners will transmit a message to every listener in the list. For example, when SetCurrentTime is called for a particle, it in turn calls TellListeners with msg_TimeChanged.

SOM_Scope void  SOMLINK EduParticleSetCurrentTime(
 EduParticle *somSelf, Environment *ev, double time)
 EduParticleData *somThis = EduParticleGetData(somSelf);
 _fTime = time;
 ba._length = ba._maximum  = 0;
 ba._buffer = kODNULL;


SOM_Scope void  SOMLINK EduObjectTellListeners(
 EduObject *somSelf, Environment *ev,
 long message, ODByteArray* param)
 EduObjectData *somThis = EduObjectGetData(somSelf);
 if (! _fIsTelling) return;
 ListenerIter  iter(_fListenerList);
 for(   EduObjectListener *thisListener = iter.First(); 
 thisListener = iter.Next()) {

Conclusion: Towards Fast, Flexible Suites

The model-view-controller technique used here to implement fast, object-based data sharing is a common technique within many monolithic applications. It is powerful because it separates the data model (particle) from its interface (viewer components), while providing change notification techniques (listeners) to keep everything nicely synchronized. It allows a suite of editors and viewers all to operate and display the same data.

Before OpenDoc, there was no way to use this technique outside the bounds of a monolithic application. SOM, in particular, makes it possible to share objects across separately authored and compiled components. SOM makes it possible to distribute an object library and the object interfaces for a specific data type. Then anybody can write a component to view and edit that type of data. (And in the future, with DSOM, objects can even be distributed across the Internet).

OpenDoc and SOM provide a powerful way to enable a group of small developers to collectively produce a software suite. For example, four projects have already contributed educational components based on EduObject. Together we are quickly building a powerful suite for physics and calculus, each contributing based on our particular strengths. The components in this suite integrate smoothly, and will provide teachers much flexibility in customizing their presentation to their students.

(You can contribute an educational part too! How about a nice analog clock, or a strip chart, or a speedometer? If you feel inspired, I’d love to see a copy of the result.)

Notice, however, that the specifics of particles are completely separated from the general interfaces that enable data sharing. You could use EduObject, EduObjectListener, and EduObjectMgr with any kind of object - a financial transaction, a personal contact record, or a calendar entry. To do so, just define a new class that inherits from EduObject.

Moreover, there is no reason to limit this power to groups of small developers who are building a suite. A larger developer that produces a deep and rich collection of editors can benefit as well. If I were building a Personal Information Manager today, I would build each contact entry as an object, and make each possible view (e.g. calendar, address book, to-do list) be a separate component. The obvious benefit is that then I could provide customers with custom solutions by substituting components. Some might like a more powerful calendar, whereas others have specific address-book requirements. Moreover, the larger developer could use components to manage the upgrade process; each component can be separately upgraded without forcing a recompile of the project. The developer could even contract some components out, while keeping the core object and components in-house. The flexibility of components allows some powerful marketing opportunities, and the architecture of OpenDoc and SOM supports much quicker time-to-market with custom solutions.

Apple, IBM and the other OpenDoc partners have delivered a powerful architecture for developers to finally realize the promise of object technology - reuse, plug-and-play integration, and smooth evolution. Now we need tool developers to seize the day and make this architecture as accessible as the stand-alone application. Then perhaps, some day soon, reusable, high-quality educational components will be as familiar to every child as their first Lego kit, model train set, or hi-fi stereo system.


Community Search:
MacTech Search:

Software Updates via MacUpdate

HandBrake 1.0.2 - Versatile video encode...
HandBrake is a tool for converting video from nearly any format to a selection of modern, widely supported codecs. Features Supported Sources VIDEO_TS folder, DVD image or real DVD (unencrypted... Read more
PhotoDesk 4.1.5 - Instagram client for p...
PhotoDesk lets you view, like, comment, and download Instagram pictures/videos. (NO Uploads! / Image Posting! Instagram forbids that! AND you need an existing Instagram account). But you can do so... Read more
Sound Studio 4.8.6 - Robust audio record...
Sound Studio lets you easily record and professionally edit audio on your Mac. Easily rip vinyls and digitize cassette tapes, or record lectures and voice memos. Prepare for live shows with live... Read more
Capo 3.5.1 - Slow down and learn to play...
Capo lets you slow down your favorite songs so you can hear the notes and learn how they are played. With Capo, you can quickly tab out your songs atop a highly-detailed OpenCL-powered spectrogram... Read more
Google Earth - View and contr...
Google Earth gives you a wealth of imagery and geographic information. Explore destinations like Maui and Paris, or browse content from Wikipedia, National Geographic, and more. Google Earth combines... Read more
QuickBooks R12 - Financial...
QuickBooks helps you manage your business easily and efficiently. Organize your finances all in one place, track money going in and out of your business, and spot areas where you can save. Built for... Read more
Google Earth - View and contr...
Google Earth gives you a wealth of imagery and geographic information. Explore destinations like Maui and Paris, or browse content from Wikipedia, National Geographic, and more. Google Earth combines... Read more
QuickBooks R12 - Financial...
QuickBooks helps you manage your business easily and efficiently. Organize your finances all in one place, track money going in and out of your business, and spot areas where you can save. Built for... Read more
FileZilla 3.24.0 - Fast and reliable FTP...
FileZilla (ported from Windows) is a fast and reliable FTP client and server with lots of useful features and an intuitive interface. Version 3.24.0: New The context menu for remote file search... Read more
Bookends 12.7.8 - Reference management a...
Bookends is a full-featured bibliography/reference and information-management system for students and professionals. Bookends uses the cloud to sync reference libraries on all the Macs you use.... Read more

Super Mario Run dashes onto Android in M...
Super Mario Run was one of the biggest mobile launches in 2016 before it was met with a lukewarm response by many. While the game itself plays a treat, it's pretty hard to swallow the steep price for the full game. With that said, Android users... | Read more »
WarFriends Beginner's Guide: How to...
Chillingo's new game, WarFriends, is finally available world wide, and so far it's a refreshing change from common mobile game trends. The game's a mix of tower defense, third person shooter, and collectible card game. There's a lot to unpack here... | Read more »
Super Gridland (Entertainment)
Super Gridland 1.0 Device: iOS Universal Category: Entertainment Price: $1.99, Version: 1.0 (iTunes) Description: Match. Build. Survive. "exquisitely tuned" - Rock Paper Shotgun No in-app purches, and no ads! | Read more »
Red's Kingdom (Games)
Red's Kingdom 1.0 Device: iOS Universal Category: Games Price: $4.99, Version: 1.0 (iTunes) Description: Mad King Mac has kidnapped your father and stolen your golden nut! Solve puzzles and battle goons as you explore and battle your... | Read more »
Turbo League Guide: How to tame the cont...
| Read more »
Fire Emblem: Heroes coming to Google Pla...
Nintendo gave us our first look at Fire Emblem: Heroes, the upcoming mobile Fire Emblem game the company hinted at last year. Revealed at the Fire Emblem Direct event held today, the game will condense the series' tactical RPG combat into bite-... | Read more »
ReSlice (Music)
ReSlice 1.0 Device: iOS Universal Category: Music Price: $9.99, Version: 1.0 (iTunes) Description: Audio Slice Machine Slice your audio samples with ReSlice and create flexible musical atoms which can be triggered by MIDI notes or... | Read more »
Stickman Surfer rides in with the tide t...
Stickson is back and this time he's taken up yet another extreme sport - surfing. Stickman Surfer is out this Thursday on both iOS and Android, so if you've been following the other Stickman adventures, you might be interested in picking this one... | Read more »
Z-Exemplar (Games)
Z-Exemplar 1.4 Device: iOS Universal Category: Games Price: $3.99, Version: 1.4 (iTunes) Description: | Read more »
5 dastardly difficult roguelikes like th...
Edmund McMillen's popular roguelike creation The Binding of Isaac: Rebirth has finally crawled onto mobile devices. It's a grotesque dual-stick shooter that tosses you into an endless, procedurally generated basement as you, the pitiable Isaac,... | Read more »

Price Scanner via

12-inch 1.2GHz Space Gray Retina MacBook on s...
Amazon has the 2016 12″ 1.2GHz/512GB Space Gray Retina MacBook (Apple model #MLH82LL/A) on sale for $1299.99 including free shipping. Their price is $300 off MSRP. Read more
Twelve South Releases RelaxedLeather Cases fo...
Inspired by the laid-back luxury of burnished leather boots and crafted in rich tones of taupe, herb and marsala, RelaxedLeather cases deliver smart, easy protection for the iPhone 7. Each genuine... Read more
Week’s Best Deal: New 2016 13-inch 2.0GHz Mac...
Amazon has the new 2016 13″ 2.0GHz non-Touch Bar MacBook Pros on sale for a limited time for $225 off MSRP including free shipping: - 13″ 2.0GHz MacBook Pro, Space Gray (MLL42LL/A): $1274.99 $225 off... Read more
Back in stock: Apple refurbished Mac minis fr...
Apple has Certified Refurbished Mac minis available 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
Apple Ranked ‘Most Intimate Brand’
The top ranked ‘”intimate” brands continued to outperform the S&P and Fortune 500 indices in revenue and profit over the past 10 years, according to MBLM’s Brand Intimacy 2017 Report, the largest... Read more
B-Eng introduces SSD Health Check for Mac OS
Fehraltorf, Switzerland based independant Swiss company- B-Eng has announced the release and immediate availability of SSD Health Check 1.0, the company’s new hard drive utility for Mac OS X. As the... Read more
Apple’s Education discount saves up to $300 o...
Purchase a new Mac or iPad using Apple’s Education Store and take up to $300 off MSRP. All teachers, students, and staff of any educational institution qualify for the discount. Shipping is free: -... Read more
4-core 3.7GHz Mac Pro on sale for $2290, save...
Guitar Center has the 3.7GHz 4-core Mac Pro (MD253LL/A) on sale for $2289.97 including free shipping or free local store pickup (if available). Their price is a $710 savings over standard MSRP for... Read more
128GB Apple iPad Air 2, refurbished, availabl...
Apple has Certified Refurbished 128GB iPad Air 2s WiFis available for $419 including free shipping. That’s an $80 savings over standard MSRP for this model. A standard Apple one-year warranty is... Read more
13-inch 2.7GHz Retina MacBook Pro on sale for...
B&H Photo has the 2015 13″ 2.7GHz/128GB Retina Apple MacBook Pro on sale for $100 off MSRP. Shipping is free, and B&H charges NY tax only: - 13″ 2.7GHz/128GB Retina MacBook Pro (MF839LL/A): $... Read more

Jobs Board

*Apple* macOS Systems Integration Administra...
…most exceptional support available in the industry. SCI is seeking an Junior Apple macOS systems integration administrator that will be responsible for providing Read more
*Apple* Retail - Multiple Positions- Deer Pa...
Job Description: Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, Read more
*Apple* Retail - Multiple Positions - Apple,...
Job Description: Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, Read more
*Apple* Technician - nfrastructure (United S...
Let’s Work Together Apple Technician This position is based in Portland, ME Life at nfrastructure At nfrastructure, we understand that our success results from our Read more
*Apple* Mobile Master - Best Buy (United Sta...
**467692BR** **Job Title:** Apple Mobile Master **Location Number:** 000602-Columbia MO-Store **Job Description:** **What does a Best Buy Apple Mobile Master Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.