3 Replies Latest reply: Apr 23, 2010 11:43 AM by 843793 RSS

    Interface Extension Doesn't Seem to Be Working

    843793
      I'm creating a "Command Consolidator" solution that (in this case, a CRUD command consolidator), accepts Create, Edit and Delete commands and consolidates them into a list according to a variety of rules, including:

      If an Add command is consolidated and an equivalent Delete command already exists, change the Add to an Edit and drop the Delete command

      If an Edit command is consolidated and an equivalent Add command already exists, absorb the Add command into the edit and drop the Edit.

      I have the following domain model around CRUD commands:
      [http://i42.tinypic.com/4u99iu.jpg|http://i42.tinypic.com/4u99iu.jpg]

      In order to break out this logic, I've created the following concept:
      public interface CrudCommandConsolidationStrategy<Q extends CrudCommand, C extends CrudCommand> {
      
           public abstract void consolidate(Queue<Q> commands, C command);
      
      }
      The Q represents the type of commands a given consolidator supports (ThingCrudCommand for example) and the C represents the specific command to be handled. Here's an example of an implementation:
      public class DeleteCommandConsolidationStrategy<Q extends CrudCommand, C extends DeleteCommand>
                implements CrudCommandConsolidationStrategy<Q, C> {
      
           @Override
           public void consolidate(Queue<Q> commands, C delete) {
                if (this.ifEquivalentAddCommandExistsRemoveIt(commands, delete)) {
                     return;
                }
                if (this.ifEquivalentEditCommandExistsRemoveItAndAddDelete(commands,
                          delete)) {
                     return;
                }
                commands.add(delete); //OOPS!  Compilation error!  "The method add(Q) in the type Queue<Q> is not applicable for the arguments (C)"
           }
      
           private boolean ifEquivalentAddCommandExistsRemoveIt(Queue<Q> commands,
                     C delete) {
                try {
                     CrudCommand add = Iterables.<Q> find(commands,
                               new EquivalentAddCommandPredicate(delete));
      
                     commands.remove(add);
                     return true;
                } catch (NoSuchElementException ex) {
                     // Don't absorb it as no add was found
                     return false;
                }
           }
      
           private boolean ifEquivalentEditCommandExistsRemoveItAndAddDelete(
                     Queue<Q> commands, C delete) {
                try {
                     Q edit = Iterables.<Q> find(commands,
                               new EquivalentEditCommandPredicate(delete));
      
                     commands.remove(edit);
                     commands.add(delete); //OOPS!  Compilation error!  "The method add(Q) in the type Queue<Q> is not applicable for the arguments (C)"
                     return true;
                } catch (NoSuchElementException ex) {
                     // Don't absorb it as no add was found
                     return false;
                }
           }
      
      }
      As you can see, I'm getting a compilation error whenever I code a commands.add(C). I would expect this would work though as Q extends CrudCommand and C extends DeleteCommand, which in turn extends CrudCommand.

      I can get around this error by casting C to Q, but that seems like a hack:

      commands.add((Q)C)

      Any idea what I'm doing wrong?

      Thanks!
        • 1. Re: Interface Extension Doesn't Seem to Be Working
          843793
          I should mention that I've also tried this:
          public class DeleteCommandConsolidationStrategy<Q extends CrudCommand>
                    implements CrudCommandConsolidationStrategy<Q, DeleteCommand> {
          
               @Override
               public void consolidate(Queue<Q> commands, DeleteCommand delete) {
                    if (this.ifEquivalentAddCommandExistsRemoveIt(commands, delete)) {
                         return;
                    }
                    if (this.ifEquivalentEditCommandExistsRemoveItAndAddDelete(commands,
                              delete)) {
                         return;
                    }
                    commands.add((Q) delete);
               }
          
               private boolean ifEquivalentAddCommandExistsRemoveIt(Queue<Q> commands,
                         DeleteCommand delete) {
                    try {
                         CrudCommand add = Iterables.<Q> find(commands,
                                   new EquivalentAddCommandPredicate(delete));
          
                         commands.remove(add);
                         return true;
                    } catch (NoSuchElementException ex) {
                         // Don't absorb it as no add was found
                         return false;
                    }
               }
          
               private boolean ifEquivalentEditCommandExistsRemoveItAndAddDelete(
                         Queue<Q> commands, DeleteCommand delete) {
                    try {
                         Q edit = Iterables.<Q> find(commands,
                                   new EquivalentEditCommandPredicate(delete));
          
                         commands.remove(edit);
                         commands.add((Q) delete);
                         return true;
                    } catch (NoSuchElementException ex) {
                         // Don't absorb it as no add was found
                         return false;
                    }
               }
          
          }
          This approach is preferred as now I don't need to provide a generic for the delete command, but the same problem persists.
          • 2. Re: Interface Extension Doesn't Seem to Be Working
            800268
            You aren't interested in the specific type of commands in the Queue, so simply:
            public class DeleteCommandConsolidationStrategy
                      implements CrudCommandConsolidationStrategy<CrudCommand, DeleteCommand> {
            If fact it looks like you can remove the first generic parameter all together: doesn't a strategy almost always work on a queue of heterogeneous commands but must be able to modify the queue? Only Queue<CrudCommand> fits that so you can put that in the interface directly.
            • 3. Re: Interface Extension Doesn't Seem to Be Working
              843793
              True. So I've tried that. The problem then comes up with the guy that's using this:
              public class CrudCommandConsolidator<T extends CrudCommand> implements
                        CommandConsolidator<T> {
              
                   private Queue<T> commands = new LinkedList<T>();
                   private AddCommandConsolidationStrategy<T> addStrategy = new AddCommandConsolidationStrategy<T>();
                   private EditCommandConsolidationStrategy<T> editStrategy = new EditCommandConsolidationStrategy<T>();
                   private DeleteCommandConsolidationStrategy deleteStrategy = new DeleteCommandConsolidationStrategy();
              
                   /*
                    * (non-Javadoc)
                    * 
                    * @see
                    * com.leohart.cqrs.commands.CommandConsolidator#consolidate(com.leohart
                    * .cqrs.commands.Command)
                    */
                   @SuppressWarnings("unchecked")
                   public void consolidate(T command) {
                        if (command instanceof AddCommand<?>) {
                             this.addStrategy.consolidate(this.commands,
                                       (AddCommand<EditCommand>) command);
                             return;
                        }
                        if (command instanceof EditCommand) {
                             this.editStrategy.consolidate(this.commands, (EditCommand) command);
                             return;
                        }
                        if (command instanceof DeleteCommand) {
                             this.deleteStrategy.consolidate(this.commands, // ERROR: The method
                                       // consolidate(Queue<CrudCommand>,
                                       // DeleteCommand) in
                                       // the type
                                       // DeleteCommandConsolidationStrategy
                                       // is not applicable
                                       // for the arguments
                                       // (Queue<T>,
                                       // DeleteCommand)
                                       (DeleteCommand) command);
                             return;
                        }
              
                        throw new UnsupportedCommandException(
                                  "Consolidator does not support this command.");
                   }
              
                   public Collection<T> getCommands() {
                        return this.commands;
                   }
              }
              He implements this:
              public interface CommandConsolidator<T extends Command> {
              
                   abstract void consolidate(T command);
              
                   abstract Collection<T> getCommands();
              
              }
              Now I want to keep this class genericized because outside clients will use it like this:
                CrudCommandConsolidator<ThingCrudCommand> thingConsolidator = new CrudCommandConsolidator<ThingCrudCommand>();
              
                //...consolidate commands
              
                Collection<ThingCrudCommand> thingCommands = thing.getCommands();
              
               for (ThingCrudCommand command: thingCommands){
                  //...do specific thing stuff here.
               }
              If I lock this down solely to CrudCommand as we did with the strategies, I have the following problem:
                CrudCommandConsolidator thingConsolidator = new CrudCommandConsolidator();
              
                //...consolidate commands
              
                Collection<CrudCommand> crudCommands = thing.getCommands();
              
                for (CrudCommand command: crudCommands){
                  ThingCrudCommand thingCommand = (ThingCrudCommand) command;  //SADNESS!
                  //...do specific thing stuff here.
               }
              I'm trying to avoid this casting.