in reply to Re: (OT) OOUI: multiple views in an object.
in thread (OT) OOUI: multiple views in an object.

Take for example a coffee machine object {...} The coffee machine needs data input, I need to set the value of its water container to full (Ie. fill it), the coffee, the type I want, etc. pp. If I can do this only once, when I create the object, then I can only create coffee until it's empty, and then I need a new machine.. (!?)

Here's one view of how to use OO-design, with no setters or getters, to construct a coffee machine program.

The first thing is that in the real world, a coffee machine is not a component of a system -- it is the system. In a futuristic scenario, one might imagine many coffee machines being installed in a 'intelligent' or 'wired' office, but even then, they wouldn't be components of the Building object. There would be no way for the Building object to instantiate a new instance of the Coffee Machine Class on the third floor!

They might be communications partners to the Building object. The Building object might contain instances of a Coffee Machine object, but these would be proxy objects who's purpose is to represent (and communicate with) the real Coffee Machines. Not be Coffee Machines or even control their operation.

Instead of thinking of the Coffee Machine as an object to be instantiated within the application, the application is the Coffee Machine. There would be little point if having a Coffee Machine class and instantiating a single instance of it. The CM application is essentially a stand alone application, running on the real CM's cpu.

What might it look like?

It would need some way of communicating with the user in order to accept orders and report things like price, out-of-product, out-of-order etc. Dedicated lights illuminating separate panels for each output communication are the traditional way, but these are inflexible, unreliable mechanical devices which also tend to have a limited service life and are costly and difficult to replace, so most new CM's will use an LCD display screen. So we have a screen for output, but there may be environments in which a collection of neons beside labels, or bulbs illiminating panels or even audio tones would be preferable. Better to encapsulate the output device behind an abstraction and leave the detail on individual installations to deeper in the application.

Overlay the LCD with a touch sensitive panel and we have a totally configurable input device, but equally, mechanical buttons, voice commands or braille input might be better in some places. Again, encapulate that detail.

There will also be the possibility of needing to communicate with external entities besides the user. Send an email to a service dept. for a re-fill as an example. Or request mainenance. Why not wrap all external communications into an IO object.

It also needs a set of hoppers -- coffee, sugar, milk (powder), (maybe cocoa, soups and whatever). From the point of view of the CM application, these are attributes, but the complexities of detecting whether they are empty, rotated into the correct position, dispensing the appropriate volumes are details that are best encapsulated in a Hopper object. The CM application. would instantiate one instance of Hopper object for each ingredient it can dispense.

It also needs a source of hot water. This could be abstracted as a Hopper object, but it has enough differences to require it's own class. For instance. Where as a Hopper object needs external (manual) intervention to re-fill it when it runs low, the Water Object will generally refill automatically from the main. Whilst a Hopper object will need to vary the amount of ingredient it dispenses dependant upon the recipe, the Water object will often always dispense the same volume, or if it varies, it will be according to criteria input by the user (espresso versus latte or 7oz versus 10 oz etc.). The Water Object also has some extra attributes to concern itself with. Is the water hot enough (eg. first thing in the morning). So, a separate Water Class is probably called for .

It also needs a set of recipes. How much of each ingredient should be dispensed to fulfil each formula. Essentially, this is a database. A set of pre-defined inputs derived from some external source.

It also needs an Authorisation object. And no, I'm not suggesting that your boss should be contacted for his permission for you to have a drink:) This is about money, or something approximating to it. Some installations will require the appropriate amount of cash to be inserted (with or without change). Some will accept pre-purchased (or distributed) tokens. Some will take a credit card, whether the bank issued type, or company issued type. There are other possibilities too, but they can all be encapsulated by a simple Authorisation object that when asked if the transaction is authorised, replies Yes or No. If your lucky enough to work somewhere where the management are enlightened enough to consider the provision of beverages a perk of the job or simply a necessity of running a business, then the Authorisation object is just hard-wired to always say Yes.

Okay. Assuming anyone came this far, what might the Coffee Machine application look like.

Pseudo-code

import IO; import Water; import Hopper; import Recipe; import Authorisation; import Exception; import Configuration. config = new Configuration or die 'No configuration'; except = new Exception( ) or die 'No exceptions'; # ;^) try (ex) { io = new IO( config ); Water = new WaterClass( config ); hoppers{} = new Hoppers( config ); auth = new Authorisation( config ); } else { except( ex ); } while( water.ready ) { selectedRecipe = io.displayList( recipes ); if( auth( selectedRecipe ) and IO.cupReady( timeout ) ) { try (ex) { hoppers{ ingredient }.dispense( quantity ) while (ingredient, quantity) = selectedRecipe.iterate; } else { IO.displayException( ex ); delete recipe{ $_ } for grep{ $_.uses( ex.missingIngredien +t ) @recipes; IO.requestReplenishment( ex.missingIngredient ); } } else { IO.displayStatus( 'Timeout: Try again' ); } }

Imperfect

I'm not saying that is a 'fully worked' example, but the basic premise of not getting attributes from one object and then setting them into another is kind of there. Instead of querying a list of hoppers available in this machine from the config and then instantiated an instance of Hopper for each, you pass the config to the Hopper constructor and it queries the config object for whatever information it needs to construct and pass back a collection of hoppers.

Instead of querying the description text from each of the recipes, formatting them and printing them on the display, you pass the collection of recipes to the IO object and ask it to display them as a list.

When the IO object is instantiated, it gets passed the config object from which it gets told that it needs to allocate an area of the screen to display the list. It also might be told by that configuration to reserve an area for exception messages. It could also be told to display the time, location, machine number or whatever. When, at the top of the loop, the collection of recipes is passed to the displayList method, it queries each object in the collection for it's display string. This might be the description of the beverage, and the price. The IO object doesn't care, it just adds each one to the list.

The list object knows what portion of the screen it must display itself in, and it formats the display strings accordingly. This might be a drop-down list, a scrolling region of bulleted items, a table. It doesn't matter to either the Coffee Machine application, nor the recipe objects. The IO object, (and it's constituent screen object) take care of the details. The IO object just returns which ever object gave it the display token that the user selects.

An analogy

The difference between procedurally programming objects, and programming using objects may be likened to two ways you could get your car serviced. (Was that a groan I heard?:) You could take your car to the service centre, pull out your phone, call the manufacturer, have the manufacturer read the instructions to you step by step, relay each instruction to the mechanic, have him hand you back the part he's just taken off, describe the condition to the manufacturer, have him decide whether to replace it, and then instruct the mechanic to re-fit or replace the part.

The other way is that you park the car in the service centre car park, hand in the keys and tell them to service it. When they have done so, they hand you back the keys, augmented with a bill. Job done.

Okay. It's a crude and exaggerated analogy, but in OO terms, you give the Service Centre object, the Car object, along with an authorisation token and a message saying "Service it".

In essence, you should never need to query an attribute from one object to give it to another, much less query the attribute, pass it to another, retrieve the response (or modified attribute) and return it to the originating object. You pass the originating object to the other object along with a message telling it what you want done. The two objects take care of the details, while you sit and wait for the result -- either the conformation of success or an exception object that you then choose how to handle.

Purist?

If all that sounds "purist", maybe it is, but it's no more or less purist that advocating that people not use global variables, gotos or rely on side effects in structured procedural code.

A hope

Is it the way I want to program? I'm not sure yet. I have still to see a language that allows me to write well-defined 'pure' OO code without requiring me to spend as much if not more of my time concerning myself with the mechanisms of the OO process model, than I do coding the task at hand. I'm hoping for good things from P6, given what I've seen so far, both of the detail and the care and thought that is going into that detail, I think it bodes well.

Whilst in some ways P6 seems to be giving me access to a lower level, but this is always optional, and looks like it will give me a better level of control in the guts of the machine, where it is required. By carefully crafting the innards of a module or library, using the ability to more accurately define the runtime implementation, it should be possible to construct them such, that once done, I will have no concerns about them being too inefficient or bloated. In that way, once those routines are written (well), I will be able to just use them at the higher level without concern that overall systems I construct will be unnecessarily slow or bloated.

A prediction

I predict that this access to some of the lower levels, combined with perl's existing attributes of allowing me to ignore many low-level details that cause so much trouble in other languages -- allocation and freeing of memory, pointers, systems dependencies and incompatibilities etc. -- whilst also giving me perl existing high level (and even higher level) language constructs and control mechanisms for manipulating instances of the low-level objects, means that for the first time, I foresee being able to implement efficient, re-usable objects and, thanks to perl's true polymorphism, being able to manipulate those low-level objects individually and collectively without needing to construct huge, unwieldy, class hierarchies that have a separate class for every single task on the planet.

That is one, long, unwieldy sentence, but it's also one, long, unwieldy thought, so take breaths wherever seems appropriate.

I await (without holding my breath), the next apocalypse with considerable interest.


Examine what is said, not who speaks.
"Efficiency is intelligent laziness." -David Dunham
"Think for yourself!" - Abigail
Hooray!
Wanted!

Replies are listed 'Best First'.
Re: Re+:(OT) OOUI:[...] (YALABBUkP)
by castaway (Parson) on Nov 01, 2003 at 11:09 UTC
    Now thats a nice extension of my analogy!

    And it also leads me to some more thoughts on this.. What you have described seems to be the industrial type coffee machine like the big one standing in the kitchen at work, which is installed by a company, which gets hooked up to a water pipe, the electricty, etc. and refilled with cups, coffee by them, with no interaction from me (the user), whatsoever..

    My point is that the article seems to suggest that this is the only way to do it, at all. Suppose I also have a coffee machine at home. This is a much more simple affair, its not a complete-in-itself application, its just part of my kitchen, like the toaster or kettle, an object. We are still light-years away (says I), from the fully automated kitchen (or replicator), in which I just say 'make me a coffee', and it happens. In this situation I just have to get the jug, fill it from the tap, and fill the machine, etc.

    I think there's plenty of opportunity in the real programming world for complete applications-as-objects, but also for simple objects which can be included as parts of other things.

    To go back to the analogy: The part I was describing is more like your Authorisation object, which can (presumably), be used other machines as well (candy/sweet machine, anyone?). *This* object has to have some sort of in/ouput, even if its just an 'isOk', 'isNotOK'.. Am I making any sense here.. ?

    I'm all for not just using objects to hold data, but to actually manipulate that data and just say 'yes/no', but they still have to get the data from somewhere.. (Where's your 'User' Object? :)

    C.