aspose file tools*
The moose likes OO, Patterns, UML and Refactoring and the fly likes don't know how to handle different params in a command. Big Moose Saloon
  Search | Java FAQ | Recent Topics | Flagged Topics | Hot Topics | Zero Replies
Register / Login
JavaRanch » Java Forums » Engineering » OO, Patterns, UML and Refactoring
Bookmark "don Watch "don New topic
Author

don't know how to handle different params in a command.

Piotr Trochim
Ranch Hand

Joined: Dec 29, 2006
Posts: 35
Hi

I have an engine that can be turned on and has a hub something can attached to (a gear box maybe - I'm not a mechanic so please don't laugh).
As a matter of fact - the hub the engine has is shipped along with the engine so it's also a third party thing.




These are just two features that I'm using right now - but there's more. The thing is that I'm not making engines - I'm just a user - so there may be some new features added to them later on.

My client is asking me to make him a driver. He should be able to define the commands beforehands, load them into the driver and then the driver will operate the engine.

Let's skip the driver part that executes the commands for now - if the need arises, please ask - maybe I got it wrong there in the first place.

Anyway - I have two commands so far - one that turns the engine on, and the other one that attaches a device to the hub.




Ok - now to the essence of the problem.


Take a look at the AttachDevice.perform method - the comment with the asterisks.
Imagine there's a lot of code there - a lot of code that does some hub mumbo jambo.
As a matter of fact - all this method really cares about is the Hub, and not the engine.

So it would be much better off being written like this

class AttachDevice implements Command {

private Device device;

public AttachDevice(Device device) {
this.device = device;
}

public void perform(Hub hub) {
// Hub hub = engine.getHub(); <--- we don't need this any more

// ***** some code that involves hub logics *****
}

}[/CODE]

But now I dont' fulfill the interface.
There's an easy solution to that - but that's where the real problem comes:

- Suppose I have a group of these commands that operate only on a Hub.
- I also have a group of commands that operate on the Engine
- I want all of them to be processed by the same Driver


Can you please help me design something that fulfills those requirements?

Thank you very much

Best regards,
Paksas
Piotr Trochim
Ranch Hand

Joined: Dec 29, 2006
Posts: 35
Again me

A possible solution came to my mind.

We can use an Adapter to adapt those commands that operate on totally different objects.

An example of a command that operates on the engine itself - this one remains the same:



And here's a command that operates on a Hub object - we're using an adapter in this case:



Advantages of this approach are:

- in terms of SRP - each class has a single responsiblity - adapter gets us what we need to run an action, action does the rest

- in terms of LSP - the inheritance ierarchy is limited - there's a separate hierarchy for the commands that operate on Engines, and there's a separate one for those operating on the Hub - so we're not gonna run into trouble when changing some stuff in the Engine and running a Hub-related command will break.
This matters greatly given the fact that I want to be able to run ANY command using my common Driver - I can do that now.

- in terms of OCP (quite important in this case) - if we decide to add a new Hub command - we do that in a separate commands tree and we can reuse the same adapter. If we decide to do the same for the Engine tree - nothing else gets changed.
If we learn that our engine has some kind of an allmighty cog we can take advantage of - we just write an CogCommandAdapter and start a brand new tree for the cog commands...


The only disadvantage to the solution is that if I add this hypothetical "Cog" command, I'm gonnaneed two classes instead of one - an adapter and the actual command.
But adding the next "cog"-related command requires just one class to be added, so in the end it's not that bad...


I'm still looking forward to your ideas though....


Regards,
Paksas
Peer Reynders
Bartender

Joined: Aug 19, 2005
Posts: 2906
After a quick look over your posts I'm not sure that your application of the command pattern is optimal here - the user wanting to issue commands doesn't automatically translate into the use of the command pattern.

Have you studied Robert C. Martin's Mark IV Special Coffee Maker example yet? (Dependency Injection: Mark IV Coffee Maker). It may include some valuable ideas for an OO device interface.


"Don't succumb to the false authority of a tool or model. There is no substitute for thinking."
Andy Hunt, Pragmatic Thinking & Learning: Refactor Your Wetware p.41
Piotr Trochim
Ranch Hand

Joined: Dec 29, 2006
Posts: 35
Hi

Thanks for the response.

I have studied the case.

The thing is that what I need to fulfill two requirements:

1. The cycle of commands will be prepared beforehand and executed later on - maybe never, maybe with each passing second over and over again.

2. I'm not sure what other Engines the Driver will be working with and what capabilities will they have my user might want to take advantage of

The first requirement implies that I need to store the commands sequence somewhere for the later use.
That's the reason why I decided to opt for the Command pattern.

The second requirement tells me that the Engine interface is volatile and can be extended. It may be so that I only have to work with a hub today, but tommorow somebody may throw in a cog or something else, and I still need to be able to provide a way to service that, having a CLOSED component.

That's why I'm loking for a way to close the solution.


Now - Mark IV on the other hand processes the signals immediately - doesn't have a need to buffer the commands.
Also - the solution is not closed for modification in terms my component needs to deal with.
For instance what would happen if someone wanted to have a whipped cream on top of his coffe and had a spare "Whipped Cream Maker" lying around he wanted to hook up to the coffe marker.
That's the kind of change I was told to expect.


From a model standpoint - Mark IV models a coffe making process.
My component doesn't model how an engine works - it allows one to model it.


Can you please maybe point out some points in the Mark IV model that I didn't notice that maponto the cited requirements?


Once again thanks for your response

Best regards,
Paksas
Peer Reynders
Bartender

Joined: Aug 19, 2005
Posts: 2906
Originally posted by Piotr Trochim:
The second requirement tells me that the Engine interface is volatile and can be extended. It may be so that I only have to work with a hub today, but tommorow somebody may throw in a cog or something else, and I still need to be able to provide a way to service that, having a CLOSED component.


The only way you can deal with that volatility is by removing the command interface's dependency on the engine interface. You should be able to localize the dependencies on volatile aspects through liberal use of indirection ("any problem can be solved by introducing an extra level of indirection") and thorough application of the Interface Segregation Principle (ISP (pdf)).

Example:


Note that EngineMk2 may simply extend the EngineMk1 interface - so that you can still use the EngineMk1 commands - then again you may simply want an EngineOnOff interface that all engines implement. You are going to have to learn something about these engines and their devices so that you can identify what is common and what is variable.

Commonality-variability analysis

Of course you will also need some sort of a command builder that will translate the user commands to the command assemblies possibly via some external configuration file.
Piotr Trochim
Ranch Hand

Joined: Dec 29, 2006
Posts: 35
That's a very intersting approach - thank you very much

Best regards,
Paksas
Piotr Trochim
Ranch Hand

Joined: Dec 29, 2006
Posts: 35
Hi

I tried implementing your solution.

There's a small problem though - when i want to create an installation command, the code looks like this:





Now that's not cool.

That makes me know about what device I want to install on what engine when I create the command.
I might as well do it like this:



It's a lot more readable and I don't need additional interfaces and classes that obscure the design.

Moreover - as I mentioned before - the 2 requirements I have say that I need to prepare the commands before hand and that they may be operating different devices.

Having it designed like that, I need to know what device I'm operating when I create a command - so it's no good.


Can you please help me understand?


Best regards,
Paksas
Peer Reynders
Bartender

Joined: Aug 19, 2005
Posts: 2906
Originally posted by Piotr Trochim:
Hi

I tried implementing your solution.

There's a small problem though - when i want to create an installation command, the code looks like this:



Now that's not cool.


In production code you should never see code like this.
You are always supposed to separate the creational aspect from the operational aspect.



is far more likely.





That makes me know about what device I want to install on what engine when I create the command.
I might as well do it like this:



It's a lot more readable and I don't need additional interfaces and classes that obscure the design.


Actually now you have a InstallDevice class that has to know about all the possible relationships between all engines and all devices. So this InstallDevice is no longer closed for modification because a new device or a new engine may require modified installation procedures. So InstallDevice violates the Single Responsibility Principle (SRP) - it has too many reasons to change. It may change because:
  • new Engines may require different installation procedures
  • new Devices may require different installation procedures.
  • changes to existing Engines may require modifications to the installation procedures
  • changes to existing Devices may require modifications to the installation procedures



  • Having it designed like that, I need to know what device I'm operating when I create a command - so it's no good.

    Well, how and when were you planning to find out? The user command could tell you with "install MyDevice" - but you can only install "MyDevice" if you know what "MyDevice" is.
    [ April 17, 2008: Message edited by: Peer Reynders ]
    Piotr Trochim
    Ranch Hand

    Joined: Dec 29, 2006
    Posts: 35

    Actually now you have a InstallDevice class that has to know about all the possible relationships between all engines and all devices


    Ok - you're right - the code you posted really prooves your point - now I can create a command in one place and invoke it in a totally deifferent time and place.

    Well, how and when were you planning to find out? The user command could tell you with "install MyDevice" - but you can only install "MyDevice" if you know what "MyDevice" is.


    Maybe this test will tell what I had in mind:




    The point is that I don't want to keep putting engine instance into every method call - that would model a driver that operates on many engines at once. I want to make a point that my driver operates one and only one driver.

    This means that we have to delegate the moment of engine-device checking compatibility etc. till the moment when the driver executes the code.

    The way I see it, the code of the AttachDevice command would contain the calls to that factory etc., wouldn't it?


    And once again thanks for your help - I'm starting to understand this.

    Thanks a lot

    Regards,
    Paksas
    Peer Reynders
    Bartender

    Joined: Aug 19, 2005
    Posts: 2906
    Originally posted by Piotr Trochim:
    I want to make a point that my driver operates one and only one engine.


    Yes, but you want your driver to be able to work with different types of engines at different times. In your current design with "perform(Engine engine)" you may need to downcast from from "Engine" to "EngineMkVI" for one of your concrete commands (or one of its "parts") to do it's job.

    Personally I'd rather have a failure when I'm trying to create a command (or one of its parts) on an incompatible device/engine combination via a java.lang.ClassCastException on the constructor parameters rather than getting the exception later when I am trying to run the command.
     
    I agree. Here's the link: http://aspose.com/file-tools
     
    subject: don't know how to handle different params in a command.
     
    Similar Threads
    about constructors
    Constrcutors!
    Commands refactoring
    Constants and switch statement
    Abstract class with main method