Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

I am puzzled on how to model this situation. Suppose you have an algorithm operating in a loop. At every loop, a procedure P must take place, whose role is to modify an input data I into an output data O, such that O = P(I).

In reality, there are different flavors of P, say P1, P2, P3 and so on. The choice of which P to run is user dependent, but all P have the same finality, produce O from I.

This called well for a base class PBase with a method PBase::apply, with specific reimplementations of P1::apply(I), P2::apply(I), and P3::apply(I). The actual P class gets instantiated in a factory method, and the loop stays simple.

Now, I have a case of P4 which follows the same principle, but this time needs additional data from the loop (such as the current iteration, and the average value of O during the previous iterations).

How would you redesign for this case?

share|improve this question
    
I guess you meant "... have the same finality, produce O from I"? –  Steven Jeuris Feb 15 '11 at 12:54
    
@Steven : yesss.. –  Stefano Borini Feb 15 '11 at 12:55
    
Isn't this the Strategy design pattern? –  S.Lott Feb 15 '11 at 12:59
    
@S.Lott : yes, I designed it as a Strategy, but the strategy accepts different inputs. That's why it does not fit perfectly. The strategy assumes that the algos are different, but the input is the same –  Stefano Borini Feb 15 '11 at 13:01
    
@Stefano Borini: "the strategy accepts different inputs". No, it doesn't. They can trivially be made all the same. I don't understand the question. –  S.Lott Feb 15 '11 at 13:04

3 Answers 3

up vote 6 down vote accepted

I would add a single "context" parameter to the method; the context can contain the loop iteration, and any other data needed - and can be extended (by adding extra data) without breaking the API.

The strategies that don't care about the context simply don't use it - and it is only a single extra reference on the signature, not 16 arguments.

share|improve this answer

I'd simply add an empty iterationBegin(CurrentO, Index) to PBase, then override in P4. The main reasoning (assuming a somewhat more complex scenario than your sample case) would be to both avoid cluttering apply with unneeded arguments, and to avoid changing the existing PBase subclasses.

If you later on need more than just the O and the Index, using the same approach with methods like setAverageO, setLoopIndex will allow you to pick up only what is needed in specific PBase subclasses.

share|improve this answer
    
Yuk at virtual functions when abstract is a viable solution‌​. Also, you should always try to prevent shared state. In your case the iterationBegin() call will force you to store these parameters. You will have shared state between iterationBegin() and the apply() call, where the order of those calls is important. –  Steven Jeuris Feb 15 '11 at 13:49
    
@Steven Jeuris:Making such functions pure virtual would require changing all PBase subclasses, which I'd want to avoid. Sure, for 4 concrete classes, who cares, but for a more complex scenario with e.g. a few hundred subclasses, avoiding changes to all classes due to the requirements of one new subclass would be preferable. The "Context" mechanism mentioned below and the method I describe allow changing requirements with minimum modification of existing code. –  Erik Feb 15 '11 at 14:05
    
@Steven Jeuris: On the topic of shared state, this is IMO an acceptable tradeoff, as it is both easy to detect when the expected state is not provided to the P4 class, and there's a single rarely changing point (the primary loop) where you need to ensure that the state is passed to PBase subclasses. –  Erik Feb 15 '11 at 14:12
    
@Erik: The fact that there is almost never a need to override a virtual method indicates a wrong abstraction, hence my posted answer. Using a context parameter simply moves the need to know specific types to where the context parameter is interpreted. This is similar to my proposal to make the loop aware about different P's. Either could be good solutions, depending on where most change is expected. Ofcourse, when the context could be encapsulated inside a specific implementation, this is still better. –  Steven Jeuris Feb 15 '11 at 14:17
1  
@Steven Jeuris: I completely agree on the indication of wrong abstraction, and I wouldn't choose this as a design. My answer is more focused on the "redesign" context, where my primary goal would be to make an existing design extensible without breaking anything or requiring large amounts of changes. I think we're basically agreeing on how this can/should be solved, but answer based on different primary goals. –  Erik Feb 15 '11 at 14:27

Is there any reason why passing the current iteration to P1, P2 and P3 would seem 'wrong'? If these parameters would be relevant, but simply aren't necessary, I would simply pass them, so that other implementations can use them.

If the current iteration is something which isn't related at all to P1, P2 and P3 but only to P4, you should probably use a different design, where the loop is aware of different P's. P's that need no iteration awareness, and P's that do need it.

I'm guessing the first design which simply passes iteration parameters is OK.

UPDATE relating to comment:

In case you need parameters which aren't specific to the iteration, and can be scoped entirely inside a specific type (or abstract subtype) of P, be sure to do so.

share|improve this answer
    
what I don't like is that if more and more P are added, each one with other parameters, the convergence would be that all Ps accept a bunch of data they don't need, and have to extract only those which are needed (potentially just one or two) –  Stefano Borini Feb 15 '11 at 13:01
    
@Stefano Borini: If this were to be the case, that it's not just simply some iteration parameters. (current, total, step?) You should consider a redesign. What other parameters would you have to pass? You could store these parameters, not relevant to the iteration, inside the specific implementations as members where necessary. –  Steven Jeuris Feb 15 '11 at 13:04

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.