Dan North brings up the "computers sending messages" metaphor - how does it line up with GOOS?

182 views
Skip to first unread message

Grzegorz Gałęzowski

unread,
Aug 6, 2021, 1:37:28 PM8/6/21
to Growing Object-Oriented Software
He stated his opinion in https://twitter.com/tastapod/status/1422967581628157952 that in object-oriented programming, objects should not model entities (like Account), but operations that act on data. He then called this approach "tiny computers sending messages". In other tweets, like this one:  https://twitter.com/tastapod/status/1422970668283535360 , he seems to me to state an opinion that a message is just data and entities should also be just data ( https://twitter.com/tastapod/status/1422971501171650561) .

This looks to me as a different take on the computer metaphor than the one I read in GOOS. From what I remember in GOOS, sending a message was equivalent to a "method call", e.g. when I call obj.toString(), I send a toString message to an obj.

I asked him how that lines up with GOOS by giving him the Train example ( https://twitter.com/GGalezowski/status/1423299905024675840) from GOOS where a it shows a class that is behaviorful AND models an entity at the same time. His answer  can be viewed here:  https://twitter.com/tastapod/status/1423301016167067655.

So given this context, here are my questions:

1. Does GOOS say that we shouldn't model entities as behaviorful objects? I think no, but it may as well be that I have misunderstood GOOS for the whole time.
2. Is my understanding of how message passing described in GOOS (that I mentioned earlier) accurate?
3. What do you think in general about Dan North's take on the "computers sending messages" metaphor? It it possible to reconcile it with GOOS or does it stand in contrast?

Best regards,
Grzesiek 



Nat Pryce

unread,
Aug 7, 2021, 7:06:04 AM8/7/21
to growing-object-o...@googlegroups.com
The "objects are little computers that send each other messages" is Alan Kay's metaphor.  Our approach in GOOS was heavily influenced by Kay's idea.

I see no difference between GOOS and what Dan is saying.  In (most) OO languages, messages carry tuples of data (values and references to objects) and the objects have the "smarts" -- each object dispatches the messages it receives to its method of reacting to the message.

"Does GOOS say that we shouldn't model entities as behaviorful objects". You'd have to define what you mean by "entity".  One meaning of the term is a behaviorful object with identity (in contrast to a value).  In which case, the question is tautological.  Should concepts in the domain be modeled as behavioural objects -- sure.  Should concepts in the domain that are modelled as behavioural objects also relate one-to-one with persistent records in a database?  No.

The answer to any design question depends on the context.  What is the simplest organisation of data and behaviour for the context in and for which you are writing the code?

--Nat


--

---
You received this message because you are subscribed to the Google Groups "Growing Object-Oriented Software" group.
To unsubscribe from this group and stop receiving emails from it, send an email to growing-object-oriente...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/growing-object-oriented-software/6eab1af9-6dce-4f96-80fe-c40638ae6218n%40googlegroups.com.


--

Grzegorz Gałęzowski

unread,
Aug 7, 2021, 1:15:48 PM8/7/21
to Growing Object-Oriented Software
Thanks for the reply.

What follows is my interpretation of what Dan said (based on the twitter answers I linked to and others in this thread)

The term "entity" was used by Dan (" object _isn't an entity_, it is the operation itself; "little computers passing messages". Objects _operate on_ things, they don't _represent_ things"). I think the last part of the second sentence suggests that he means by "entity". The example he gives of an entity that should not be an object is "account" (because account is a "thing", not an operation/set of operations). From his later tweets I take that he considers design such as this one "incorrect":

class Account {
  void transferFundsTo(Account anotherAccount, Dollars amount) { ... }
  void add(Dollars amount) {}
}

Instead, he proposes the following design:

class FundTransferer {
  void transferFunds(TransferFundsMessage message) {}
}

where TransferFundsMessage is a data structure (a record?) that contains ids of both accounts and the amount of money. This reminds me more of "erlang way of doing things" which Dan also points to as his inspiration. In his proposed design, account would not be a "computer sending messages", just aprojection of a set of transactions (which I interpret as he means a data storage-specific concept).

Now, he justifies his proposed design in two ways:
1. That the operation itself does not belong to an account because it's much more complicated and may have some rules asssigned. Also, none of the accounts would "own the whole of the operation". Here, I find it hard to argue because I don't work in finance and maybe that's how the domain plays out.
2. That objects should model operations (hence the "transferer" is the main actor here, not an account), not "things". This one seems like a general rule he is giving and if so (remember most of this is my interpretation), I find it hard to accept a general rule like that.

So I think I see some differences between how I understand him and how I think I understand the OO approach in GOOS. 

Best regards,
Grzegorz

Nat Pryce

unread,
Aug 8, 2021, 6:11:41 PM8/8/21
to growing-object-o...@googlegroups.com
I am still in the dark by the distinction that is being drawn between "entity", and something else, and what that other thing is (if not a value).

Take the bank account transfer example...

An account is a history of transactions (debits and credits).  The balance of an account is calculated from that history.

Transfer of money between accounts involves asynchronous messaging, reconciliation processes that check that the money movements occurred as expected, and business processes that happen if they do not.  Different systems keep track of their state machines for coordinating the transfer, data that must be reconciled to transition the state machines, and data that is needed by banking staff to investigate and fix exception cases.  

I don't see why a "transfer" (or at least, the viewpoint of a transfer in every involved system) is in any way less an entity than an account (or at least, the viewpoint of an account in every involved system).

--Nat












--

Peter DiSalvo

unread,
Aug 9, 2021, 4:46:05 AM8/9/21
to Growing Object-Oriented Software
I've struggled with this kind of thing for a long time. I still don't really know the "right" way to do it. Sorry if everything I'm about to say sounds like nonsense. There's a good chance I have no idea what I'm talking about.

There's a quote from Sandi Metz's book, Practical Object-Oriented Design in Ruby, that has always stuck with me:

"This transition from class-based design to message-based design is a turning point
in your design career. The message-based perspective yields more flexible applications
than does the class-based perspective. Changing the fundamental design question
from “I know I need this class, what should it do?” to “I need to send this message,
who should respond to it?” is the first step in that direction.
You don’t send messages because you have objects, you have objects because you
send messages."

Still, the question is who can respond to the transferFunds message? I suppose a FundTransferer object will do. It shouldn't matter to the sender. In a dynamic language, the sender is coupled to the message name and arguments. In a static language with interfaces, it might be coupled to an interface and message, e.g.

interface CanPerformFundsTransfer {
  void transferFunds(Amount amount, AccountId fromAccountId, AccountId toAccountId);
}

In Grzegorz's example he used the TransferFundsMessage type to represent the message and its data and referred to erlang as an example. I'm not familiar with erlang, but I'm reminded of Smalltalk. In the same way the TransferFundsMessage type statically defines the message (action + data), Smalltalk messages are a combination of action and data, closer to the erlang model. IIRC, erlang objects run on their own processes and implement actor-like behavior which is what makes it different, but I think the message concepts in Smalltalk/Erlang are closer than say Smalltalk/Java.

In a language like Java, the transferFunds method is not really a thing like it is in Smalltalk, so to represent the message itself we could create a typed data clump like Grzegorz's example of TransferFundsMessage type that actually represents the message (unique type name representing the action + data properties).

In Smalltalk, we could have a message selector like #transferAmount:fromAccount:toAccount:. This is a unique symbol representing the message and associated data (arguments), in the same way the TransferFundsMessage type has a unique name and properties.

I think what's more interesting is assuming you've defined the interface as FundsTransferrer::transferFunds, how is it implemented? If we take the same perspective as messages first, we might practically end up with something like what Justin Searls does with his discovery testing technique... https://blog.testdouble.com/talks/2015-09-10-how-i-use-test-doubles/ which, for the sake of keeping things simple, might end up looking something like this:
 
class TransfersFunds {
  private ValidatesFundsTransfer validatesTransfer;
  private ProvidesTransferId providesTransferId;
  private CompletesTransfer completesTransfer;
  
  TransferResult transferFunds(TransferFundsMessage message) {
    ValidationResult vr = validatesTransfer.isValid(message.fromAccount(), message.toAccount(), message.amount());
    
    if(!vr.isValid()) {
        return TransferResult.failed(vr);
    }
    
    TransferId id = providesTransferId.newId();
    return completesTransfer.transfer(id, message.fromAccount(), message.toAccount(), message.amount());
  }
}

Those other objects would be further broken down into responsibilities. Eventually, they would do some actual work, but mostly they delegate the work to these anonymous agents. You'll notice with his technique, if you read Justin's blog/watch the videos, there are very small objects, each with only one or two methods. There is a pattern of implementing classes with an input, process, and output collaborators. It's an interesting technique, but I look at it and think... The things like ValidatesFundsTransfer, ProvidesTransferId, and CompletesTransfer can be renamed to FundsTransferValidator, TransferIdProvider, and TransferCompeleter. To me, this is reminiscent of procedural or service oriented designs where data clumps are passed along anonymous agent objects that poke at getters and setters. Lots of ThingDoer objects, with doThing() methods, which lots of popular OO literature says to avoid. At the same time, this sure as hell prevents you from having to make any decisions about what the "actual work" looks like, since we're just passing the buck via delegation.

In looking at different ways objects can communicate, I've noticed we typically separate objects into categories of entities and values. Entities having identity and state and values having immutable state and their state == their identity. I think in GOOS, we have the advice "tell objects, ask values", making that distinction between the two categories. But there is another category that I see, which I call "agents". I think this is the "something else" that Nat was referring to, RE: "what that other thing is (if not a value)." Agents don't have identity or state and they tend to represent an anonymous operation or another system that can just "do things". This is the category that the ValidatesFundsTransfer and associated objects from my example fall into.

I think if we turned the funds transfer concept into an entity by giving it identity and state, it might look something like this:

// This is likely a incorrect model without knowing the actual constraints of financial transfers,
// but it works for the purpose of discussion in contrasting the above TransfersFunds agent to an entity
class FundsTransfer {
  private TransferId id;
  private AccountId fromAccountId;
  private AccountId toAccountId;
  private Amount amount;
  private TransferState state;

  FundsTransfer(...) {
    this.id = TransferIds.new();
    this.fromAccountId = fromAccountId;
    this.toAccountId = toAccountId;
    this.amount = amount;
    this.state = TransferState.unexecuted();
  }
  
  void execute(TransferSource source, TransferDestination destination) {
    source.requestDebit(fromAccountId, amount);
    destination.requestCredit(toAccount, amount);
    this.state = TransferState.pending();
  }
}

Thoughts?

--
Pete

Grzegorz Gałęzowski

unread,
Aug 11, 2021, 4:28:19 PM8/11/21
to Growing Object-Oriented Software
Nat, thanks for your comment. Indeed, without precise definition of entity we can only further and further speculate which will probably not add much. I'd say my line of thinking is similar to yours - I view the division between "operations" and "things" as blurry at best.

Pete, thanks for your comment. I didn't know SmallTalk was designed in the way you describe - an interesting thing to note. I agree that "transferer" (and completer etc.) smell like procedural programming, but without knowing exactly what Dan had in mind, I can't for sure if that's the direction he would go into. By the way, I guess the FundTransfer object you came up with is similar to a command as in GoF Command pattern. It sends messages to source and destination that look like they are represented as "computers receiving messages" in my mind. A similar example that I gave Dan.

Best regards,
grzesiek

Reply all
Reply to author
Forward
0 new messages