Hey,

The previous post showed how to wrap a ManyAssociation so that we could 
add concerns to the methods and hide the internals somewhat. However, it 
would be nice to be able to hide the internal state entirely, using 
something similar to private mixins.

Here's what I want to be able to do in client code:
Person person;
{
     EntityBuilder<Person> builder = unitOfWork.newEntityBuilder( 
Person.class );
     builder.stateOfComposite().name().set( "Rickard" );
     person = builder.newInstance();
}

CompanyEntity companyEntity;
{
     EntityBuilder<CompanyEntity> builder = unitOfWork.newEntityBuilder( 
CompanyEntity.class );

     companyEntity = builder.newInstance();
}

Employee ceo;
{
     EntityBuilder<Employee> builder = 
unitOfWork.newEntityBuilder(Employee.class);
     builder.stateOfComposite().position().set( "CEO" );
     ceo = builder.newInstance();
}

Employee cto;
{
     EntityBuilder<Employee> builder = 
unitOfWork.newEntityBuilder(Employee.class);
     builder.stateOfComposite().position().set( "CTO" );
     cto = builder.newInstance();
}

companyEntity.employees().hire( person, ceo );
companyEntity.employees().reassign( person, ceo, cto );
companyEntity.employees().fire( person, cto );
---
These last few lines are the critical ones. Instead of using "add" and 
"remove" in ManyAssociation, the Employees domain object actually has 
domain methods such as "hire" and "fire". This entirely hides the 
internal collection. This is implemented like so:
// Base domain Entities are straightforward
@Mixins( CompanyMixin.class )
interface CompanyEntity
     extends EntityComposite
{
     Employees employees();
}

interface Person
     extends EntityComposite
{
     Property<String> name();
}

interface Employee
     extends EntityComposite
{
     Property<String> position();
}

// This time Employees is a regular Composite
// with an EmployeesMixin
// Concerns can still be added, of course
@Mixins( EmployeesMixin.class )
@Concerns( EmployeesAuditConcern.class )
interface Employees
     extends Composite
{
     void hire(Person p, Employee e);
     void fire(Person p, Employee e);
     void reassign(Person p, Employee oldEmployee, Employee newEmployee);
}

// The CompanyMixin instantiates the Employees domain composite
public static abstract class CompanyMixin
     implements CompanyEntity
{
     Employees employees;

     public void init( @Structure CompositeBuilderFactory cbf, @This 
CompanyState company )
     {
         // Take the state and pass it into the Employees object
         this.employees = cbf.newCompositeBuilder( Employees.class 
).use( company.employees() ).newInstance();
     }

     public Employees employees()
     {
         return employees;
     }
}

// Private mixin for company state
interface CompanyState
{
     ManyAssociation<AssociationRole<Person,Employee>> employees();
}

// Implementation of Employees
// Domain methods call the collection internally
// Also ensures that unused roles are removed properly
public static abstract class EmployeesMixin
     implements Employees
{
     @Uses ManyAssociation<AssociationRole<Person,Employee>> employees;

     public void hire( Person p, Employee e )
     {
         employees.add( role(p, e) );
     }

     public void fire( Person p, Employee e )
     {
         employees.remove(role(p, e));
         e.remove();
     }

     public void reassign( Person p, Employee oldEmployee, Employee 
newEmployee )
     {
         employees.remove( role(p, oldEmployee) );
         oldEmployee.remove();
         employees.add( role(p, newEmployee) );
     }
}

// Audit concern for Employees coming and going
// This is more powerful than before since it can
// log the "reassign" event properly, which before
// would just have been a sequence of remove+add.
public static abstract class EmployeesAuditConcern
     extends ConcernOf<Employees>
     implements Employees
{
     public void hire( Person p, Employee e )
     {
         System.out.println("Hired "+p.name()+" as "+e.position());
         next.hire( p, e );
     }

     public void fire( Person p, Employee e )
     {
         System.out.println("Fired "+p.name()+" as "+e.position());
         next.fire( p, e );
     }

     public void reassign( Person p, Employee oldEmployee, Employee 
newEmployee )
     {
         System.out.println("Reassigned "+p.name()+" from 
"+oldEmployee.position()+" to "+newEmployee.position());
         next.reassign( p, oldEmployee, newEmployee );
     }
}
---
That concludes the quick test I did of this. Is this something along the 
lines of what we want? Any pros/cons to it, and can it be improved further?

/Rickard

_______________________________________________
qi4j-dev mailing list
[email protected]
http://lists.ops4j.org/mailman/listinfo/qi4j-dev

Reply via email to