On 24 Jan 2023, at 16.30, Dom <dom.spi...@gmail.com> wrote:EDCI adds Events to DCI. I'll try to explain what this means through a series of examples.
Let's start with a simple motivating example. Suppose we have an application with a GUI with buttons which should trigger actions in an application when pressed.
The examples in trygve that use buttons [1] have an event mechanism that requires contexts to inherit an event handling interface and be registered with something that will raise events by calling the registered handlers.
The context must implement the interface's methods that will be called in response to events and decide what to do with them, associating them with buttons or whatever. This is easy with one button, but becomes more involved if there are many.context BorrowLibraryItems implements ButtonHandler {// ...public void handleButtonPress(Object button){CheckoutList.removeAll();
}}
Now imagine instead that we could have a button object play a role in a context and have the button itself communicate to its role that it has been pressed.
This would directly associate each button with its purpose.
Events provide a way to do this.DCI already has the concept of a contract between a role and the role playing object. The contract specifies what abilities the role requires of an object for it to play the role. In practice this is consists of the signatures of the methods that the role requires the role player to provide.
Suppose we extend the contract to include events that the role expects the object to signal to it. It would specify the signature of events that the object is expected to raise. The role would provide role methods matching the signatures of the events to handle them. These would be invoked when the event happens.
In the case of our button the contract would include a button pressed event.Here a cancellation button is directly associated with the action of clearing the CheckoutList:context BorrowLibraryItems { // now no need for buttonHandler interfacerole CancelButton {// Handle a buttonPressed event from the role playervoid on( buttonPressed, <any other event args...> ) {CheckoutList.removeAll();}} requires {event buttonPressed( <any other event args...> ); // require that the object provides a buttonPressed event}
Now the role of each button can be clearly and directly expressed.
Since an object such as a button has no knowledge of how it might be used, any button pressed event would be passed to all the roles that the button might be playing.
The assumption here is that the language/runtime system tracks the roles that each object is playing and can invoke the appropriate role methods in each role in each context in response to the event.
Imagine that our application is for designing birthday cards.
We have a number of different UI sections containing buttons. Each section might be implemented as separate contexts each containing a number of buttons.
To add a whimsical element we want our app to play successive notes of "Happy Birthday to You" whenever a button is pressed anywhere in the app.
One implementation strategy would be for all the buttons to play a role in a PlayHappyBirthday context that would respond to any button press by playing the next note in the tune. We would use an aggregate role such as a vector or a set to which all the applications buttons could be added.context PlayHappyBirthday {role [] Buttons {void on( buttonPressed, <any other event args...> ) {Tune.playNextNote();}} requires {event buttonPressed( <any other event args...> );}By allowing buttons to play roles both in UI contexts and in the tune playing context we can neatly encapsulate the different pieces of functionality.
Should we want to drop or change the tune playing feature we would find the code for this to be entirely self-contained.
The event mechanism provides two benefits:
1. Clearer association between buttons (events) and the role they play in the application.
2. Separation (shearing layers) between different areas of functionality: UI elements using buttons and our whimsical tune feature.Such an Events mechanism is a natural fit with the asynchronous external nature of UI interactions, but the underlying mechanism is very general and broadly applicable.
I'll expand on this with further examples...
--
You received this message because you are subscribed to the Google Groups "object-composition" group.
To unsubscribe from this group and stop receiving emails from it, send an email to object-composit...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/object-composition/ad3059fb-3191-464e-a66a-409d577c4be6n%40googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/object-composition/8BA0BC6E-A94E-45A9-B23E-AE8BCFBC6514%40gmail.com.
On 25 Jan 2023, at 14.58, Dom <dom.spi...@gmail.com> wrote:I don't mean to pick on trygve, but am looking for concrete examples to contrast.
I can't find any reference material or examples for C84, as far as my Google-Fu says there was never formally any such language/spec.
/*
* J_SI0.cpp
* PFASimulationModel
*
* Created by James O. Coplien on 5/7/07.
* Copyright 2008 Gertrud & Cope, Mørdrup, Denmark. All rights reserved.
*
*/
#include "J_SI0.h"
#include "Server.h"
#include "ScreenLog.h"
#include <math.h>
#include "PFAIDocsMessage.h"
#include "S_SAPFICOIN_Daemon.h"
static const PFATimeInterval RUNTIME("00:18");
// main
void
J_SI0::Body(void)
{
// Job started by AIA when it is done to convert a file to
// messages for sending to SAP-FICO
// no standardBatchAnnounce()!
(*systemLog) << "SI converting AIA file to I-DOCS messages" << std::endl;
// Attach myself to a view
// PFAView *SI0View = viewConnect("SI0");
// Attach myself to my machine
assert(server_ != NULL);
core_ = server_->jobIn(this);
assert(core_ != NULL);
// Set up things about myself that I know
cpuTimeNeeded_ = RUNTIME; // run for two minutes
S_SAPFICOIN_Daemon *SAPFICO = NULL;
for (;;) {
// Send a message to the SICO service process
if (!SAPFICO) {
SAPFICO = dynamic_cast<S_SAPFICOIN_Daemon*>(Job::findByName("QJC_SAPFICOIN_Daemon"));
// it's a daemon, so we shouldn't have to activate it
}
assert(SAPFICO != NULL);
// right now, swamp it with one message per time slice
PFAIDocsMessage *mes = new PFAIDocsMessage();
for (int j = 0; j < 100; j++) {
// send 100 of them per second (time slice interval)
// FIXME: These should come from AWB Output Record Input (which I break up and spool out)
SAPFICO->recordsFromAIA_AWBThroughSI(mes);
}
if (standardEndCheck() == false) {
break;
}
}
// standardExit(SI0View);
}
PFAMonitorData
J_SI0::value(void) const
{
const double pi = atan2(1.0, 1.0);
double percentageDone = double(cpuTimeUsed_) / RUNTIME;
double normalizedValue = 2 + 16 * sin(4 * percentageDone * pi);
return normalizedValue;
}
J_SI0::~J_SI0()
{
// nothing yet.
}
J_SI0::J_SI0(ConfigurationParameters *configObject,
Server *server, Schedule *schedule, std::string name,
const PFATime startTime, const PFATime endTime,
const PFATimeInterval cpuTimeNeeded, Job::JobType jobType):
Job(configObject, server, schedule, name, startTime, endTime)
{
jobType_ = jobType;
cpuTimeNeeded_ = cpuTimeNeeded;
}