amolenaar / roles

Library for Role based development
BSD 3-Clause "New" or "Revised" License
57 stars 7 forks source link

Roles interacting with Roles or with objects? #4

Closed creative-resort closed 9 months ago

creative-resort commented 3 years ago

Hello Arjan,

I'm looking at the example https://github.com/amolenaar/roles/blob/24be07e2573b464c619fd87dbb22a53967452d1e/example.py#L37-L41 and have the following question:

Shouldn't roles only be interacting with other roles? However, the MoneySource role is interacting with the instance "sink", which the MoneySink role will be bound to only some time later in the context class TransferMoney.

Let's think about it: MoneySource prescribes, which object MoneySink must be bound to in context ... What if that name changes in the context class? Isn't the point of DCI roles to decouple roles from object instances? This is a coupling between object, context and role, that shouldn't be happening before the binding in the context class takes place, correct?

The way it's now, doesn't seem to make sense to me, but I could be wrong. What are your thoughts on this?

amolenaar commented 3 years ago

Hi @creative-resort,

First of all: thank you for your interest in this this package :)

I had to refresh my memory.

https://github.com/amolenaar/roles/blob/24be07e2573b464c619fd87dbb22a53967452d1e/example.py#L49-L62

The TransferMoney object is where the whole play is staged. A context is created that applies the MoneySource and MoneySink roles to the source and destination account. As a result source and sink are available in the context.

Since MoneySource is talking to the sink account via the context, it is in fact talking to the MoneySink role. Maybe it would be nicer to provide the sink as an argument to the transfer role method instead?

amolenaar commented 3 years ago

@creative-resort I did a few updates to the example file. Is the intent more clear now?

creative-resort commented 3 years ago

Yes Arjan, your package seems very important to me, as DCI (the context aspect in particular) is otherwise missing from Object Oriented programming in Python — at least: a systematic approach to it.

Thank you for making the intent more clear. I understand how you've coupled roles and instances and the exemplary roleplay. As I'm looking at the updated example, I am still missing a couple of tenets of DCI, as James Coplien teaches it:

What I notice in the example, however is:

  1. Right now it is possible within a Role, to call another Role's bound object's (non-role) methods. e.g. within Role MoneySource I am able to call context.sink.deposit(amount), which should result in an access error.

  2. In the updated example, you're explicitly telling the source where to transfer the amount. It should not be necessary to provide that parameter, as the meaning of a "sink" is semantically already clear within the MoneyTransfer context. Instead, it should be context.MoneySink.receive(amount) within MoneySource.transfer(). Not the bound object names should be on the context stack, but the Roles. In my view, only the receive method (green in the following screenshot) should be available, and instead of sink (red) it should be MoneySink. 2021-10-21_170615

  3. The MoneyTransfer class itself is not yet a context but only implicitly becomes a context once a context is created inside of it. Though I'm still pondering whether this would finally be a good idea, using a Context metaclass.

Do these points make sense to you?

Here is a code snippet I've created, that shows a bit more of how I'd expect DCI programming to function. See following draft pull request for further discussion: https://github.com/amolenaar/roles/pull/5/commits/2d775895cc3df74981422409c29c2887746c0b3a