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

I've only just read the first page of the article, and already I'm starting to wonder if he isn't overdoing it a little in the 'object purity' department.

Not that I don't see what he's getting at, but it doesn't really apply to the real world (and that of programming). For example, he says that get and set methods are evil. That would mean to me that objects aren't reusable in his world.

Take for example a coffee machine object (this is how a former colleague explained OO to a PHB, worked quite well :) - 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.. (!?)

I also have a separate object, a cup/mug/glass/whatever, into which I wish to put my coffee. What does the machine know about cups? Nothing! I should merely tell it to output the coffee, and let me deal with where it lands. A coffee machine shouldn't have to also produce cups, or whatever. (And theres another case in point, we have here coffee machines which output a plastic cup filled with coffee. Most people take a real cup, and tip the coffee into it, throwing away the plastic one. Thus creating unnecessary waste.)

An object should do the job it was designed to do (manipulate data, whatever), and not 57 other vaguely related ones..

(My Opinion)

C.

*Wondering how the article writer populates his objects, should go back and read the rest...*

  • Comment on Re: (OT) OOUI: multiple views in an object.

Replies are listed 'Best First'.
Re+:(OT) OOUI:[...] (YALABBUkP)
by BrowserUk (Patriarch) on Nov 01, 2003 at 06:08 UTC
    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!

      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.

Re: Re: (OT) OOUI: multiple views in an object.
by rir (Vicar) on Oct 31, 2003 at 14:55 UTC
    get and set methods are evil.

    There is some truth to this. I am not familiar with the article so I don't know Holub's perspective. (Update: I have read articles by Holub in the same vein.)

    The idea that an attribute should be indistinguishable, at the interface level, from equivalent fetch and set methods is extremely convenient in a language.

    class SAMPLE is data:INT is return something; end; data( param:SOMETYPE) is do_something( param) end; end
    is the same as
    class SAMPLE is attr data:INT; end
    To not use set and fetch methods is problematic in languages that do not have this feature when the interface of a class may change. Many languages require invasion of clients to change an attribute to a method. Programmers choose methods in defence against change.

    I suspect that Holub's complaint is that a lot of fetch and set methods are either being added thoughtlessly or show a poor object design abstraction. In some languages preserving a flexible interface is a strong counter-argument to the former. I agree with the latter but know there are exceptions, e.g. users wanting to "emulate" a paper trail.

Re: Re: (OT) OOUI: multiple views in an object.
by sauoq (Abbot) on Nov 01, 2003 at 18:52 UTC
    A coffee machine shouldn't have to also produce cups, or whatever.

    Beyond that, I would say the designer of a coffee machine should make as few assumptions about cups as possible.

    And I've got a case in point too. Recently, we got rid of one of those machines that outputs a little cup filled with coffee and replaced it with one where you insert your mug, stick a little packet in this slot, choose one of three "bodies" (espresso, regular, or choco) and the machine outputs the coffee directly to your cup. That would seem like an improvement right? Except that just the other day I found that my travel mug doesn't fit in the machine. Now, I can't even get the output in a little cup and pour it into my travel mug.

    Wasteful or not, with the old machine I got my coffee. Now, it's true that in the real world, where you find coffee machines and the addicts who frequent them, it is arguably more important to do away with waste than it is for every caffeine junkie to get his coffee. But, in the world of software, a working coffee machine object that was a little wasteful would likely trump a super-efficient one with a bug such that, under certain conditions, it would not allow you to fill your cup object.

    -sauoq
    "My two cents aren't worth a dime.";
    
Re: Re: (OT) OOUI: multiple views in an object.
by BUU (Prior) on Oct 31, 2003 at 19:13 UTC
    For your first point, my take on the article was that his preferred method for dealing with your coffee machine problem would be to just have a method that does what you need it to, say, $coffee_machine->refill(); Thus the specific implemenations of the coffee, how it needs to refill, etc etc are all hidden from the programmer. Other wise you would end up with something like:
    $cm->{temp}=150; $cm->{amount}=100; $cm->{heat_time}=5;
    And so on and so forth. Having just one method completely hides the implementation details of whatever you need to do. Otherwise you just have a datastructure, not an object.

    As to your second problem, in this case I would have three objects. A coffee machine object, a coffee object, and a cup object. Then you can have your coffee machine create new coffee objects and then add them to the cup object. Then you could change every single thing about coffee and cups and still be able to say
    my $cm=new Coffee_Machine; my $coffee=$cm->gimme_coffee(); my $cup=new Cup($coffee);
    And never, ever have to change those lines of code.
      Yup, I thought that's what he said too.. My point was more, how the hell does the coffee machine know how to refill itself? (Talking about a home-version, not an industrial version here, see below..)

      There are times when you want autonomous objects, and times when you want them just as parts of other apps..

      C.