Hi Rickard,
Looks very interesting! I haven't had time yet to catch up with the DCI
discussions, so I'm a little out of the loop. Some quick questions though:
- Are @Structure elements etc. injected with a ServiceImporter in the assembly
or something like that?
- If you can't inject vbf, uowf etc into the Context, wouldn't the inner
classes just be able to access those
- How does your Role class look? That's were self and bind(object) are defined,
right? Is there a self() method also? (2nd last line)
- I guess it's seldom that you would need different implementations of a Role
(mixins for an interface, the old way)
- Can you give an example of method name clashing with the old way?
Need to try it out with an example...
Cheers,
Marc
On 2011-08-18, at 08.53, Rickard Öberg wrote:
> The new way: since GroupsContext is basically the only place from which you
> would do this, and it represents a particular usecase, the "createGroup"
> method actually belongs in a role in the context, which is only valid *from
> within that context*. So, the way to do this is to implement GroupsContext as
> a class and then do the Groups commands thing as an inner class that wraps a
> Groups object that has Events+Data:
> public class GroupsContext
> {
> // Injection
> @Structure
> Module module;
>
> GroupsAdmin groups;
>
> public GroupsContext()
> {
> groups = new GroupsAdmin(); // Create wrapper
> }
>
> public GroupsContext rebind(Groups groups)
> {
> this.groups.bind(groups); // Bind wrapper
> return this;
> }
>
> public Iterable<Group> index()
> {
> return groups.index();
> }
>
> public Group creategroup( StringValue name )
> {
> return groups.createGroup( name.string().get() );
> }
>
> class GroupsAdmin
> extends Role<Groups>
> {
> Iterable<Group> index()
> {
> return self.groups();
> }
>
> Group createGroup(String name)
> {
> Group group = self.createdGroup(null,
> module.serviceFinder().<IdentityGenerator>findService(IdentityGenerator.class).get().generate(GroupEntity.class));
> group.changeDescription(name);
> self().addedGroup(null, group);
> return group;
> }
> }
> }
> ---
> The way to use this in client code would be:
> new GroupsContext().rebind(groups).createGroup(newName);
>
> The benefits with this solution are:
> * You can create any number of contexts and roles that uses the basic
> data/event methods, and don't have to worry with method clashing (major issue
> without this solution).
> * Role code can access members of the Context that wraps it, e.g. "module".
> If there were more bound objects, those could be accessed directly instead of
> having them be passed as method arguments to role methods.
> * Easier to see the algorithms as they are directly in the context, rather
> than having to go one step down into some external mixin.
> * Roles are only visible/usable from within the context that defines them.
> * Very clear what the dependencies of the context are.
> * Invisible from outside that DCI is being used.
>
> The drawbacks I can see are:
> * Since the roles are wrappers you have to think about what to pass around:
> wrapper or self() (will almost always be self() since wrapper class is
> unknown outside of context)
> * Right now cannot use concerns/sideeffects/constraints on inner classes. Can
> still use on context interaction if implemented as transient though.
>
> I'm sure there are more benefits and drawbacks, but this is what I can see
> right now. If anyone has other ideas, that would be great. IF this solution
> is acceptable, then that changes quite a bit what the typical usage pattern
> of Qi4j in a DCI setting would be, so it is kind important. It also makes it
> easier to use DCI outside of Qi4j, since there's nothing really in the above
> that requires Qi4j, from a pattern point of view.
>
> WDYT?
>
> /Rickard
>
> _______________________________________________
> qi4j-dev mailing list
> [email protected]
> http://lists.ops4j.org/mailman/listinfo/qi4j-dev
_______________________________________________
qi4j-dev mailing list
[email protected]
http://lists.ops4j.org/mailman/listinfo/qi4j-dev