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.

Assume we have an abstract class having several concrete classes which interfaces publish an information under several equivalent forms, with the difference that each class has a preferred form which can be consulted at no cost while consulting the others might require a time-consuming computation. How to implement a strategy selecting an algorithm based on this “affinity”of one class for one information form?

Example

The original example is a complicated example of a very specialised field, so I will do my best to figure out an example that will be familiar to most programmers.

Assume we have an abstract class representing I/O devices which has two concrete subclasses, one for char-based devices and one for block-based devices. Each class provides block-based access and cahr-based access, but each class has an affinity to one of these methods: block-based access is preferred for block-based devices, etc. Say, we want to implement a copy procedure, so that, if the source and the target devices are block-based then the copy is performed block-per-block, in all other cases, a char-by-char approach is preferred.

Discussion

Assume now we have not only I/O devices but several abstract classes offers access to several logically equivalent methods, but whose implementation differ greatly in perormance, thus displaying a greater affinity to one method over the others.

We want to implement a strategy working on an aggregate of such classes and selecting an algorithm taking the affinities of these classes into account.

Because of the combinatoric complexity brought by the aggregate, the visitor-pattern does not seem useful here.

Question

How can each concrete class expose its affinity for one or the other access method in a way that is useful to a strategy component picking up an algorithm suited to these affinities? The combinatorics yield a large number of combinations of affinities and the strategy only needs to consider a few “lucky cases”and use a generic algorithm to handle the vast majority of combinations.

share|improve this question
1  
This is beyond what interfaces as usually implemented can do for you. They can enforce the presence of a method, but no predilections. Any such info would have to be conveyed via a separate informative method, and even then you have to postulate an association between a particular method and some disambiguating code (e.g. an enum constant) yourself, i.e. outside the semantics of the interface itself. –  Kilian Foth Feb 3 at 12:09
    
@KilianFoth Thank you for your comment. If so, is there any common “pattern” which proved to do well in such a case? –  user40989 Feb 3 at 12:51
    
If it's possible know the combinations ahead of time, then a matrix or lookup table might be the simplest and quickest way to select the method. –  Rory Hunter Feb 3 at 12:59
    
When will the algorithm picking the correct components run? At compile time or at runtime? Does it have to be fully automatic or is it just "hint" for programmer? Should it be possible to change the components manually? –  Euphoric Feb 3 at 15:10
    
@RoryHunter Because of the combinatorics effect described in the last sentence of the question, using a matrix is not interessant: there is a generic entry and a few exceptions. –  user40989 Feb 4 at 11:22

4 Answers 4

up vote 2 down vote accepted

This kind of problem is a familiar one, but I don't think there is a single canonical solution. The two that come immediately to mind are negotiation and so-called inversion of control.

Negotiation is commonly used to choose between protocols. Your 56K modem or fax does it. So does your mail server. Essentially one service has to say to the other: "I can do A,B,C, which would you prefer?" The second service makes the choice and it is binding. In more complex scenarios there may be multiple rounds of negotiation. So it relies on a specific negotiation side channel.

There are few useful references on protocol negotiating. Try this: https://en.wikipedia.org/wiki/SPNEGO for some ideas.

IOC in this context means there is an arbitrator with a set of rules, which will call a factory on each service to return a specific concrete implementation and will then connect them to each other. This is a widely used solution in many contexts.

This is a well known technique, widely written about. This is a good starting point: https://en.wikipedia.org/wiki/Inversion_of_control.

share|improve this answer
    
Could you add references for the terms negotiations or inversion of control. (Wikipedia might do if you do not have anything better.) –  user40989 Feb 3 at 14:42

I don't think that there exists a pattern for this specific implementation. The closest you can get would be Factory pattern with some modifications. The factory pattern because the caller shouldn't really know how it is implemented for the most part, just that it gets done.

However, you're still left with the problem of selecting the proper algorithm. If you have few algorithms, then simply call the algorithm by name as @Lessat suggested in his answer. Otherwise, the way I would go about it is I would have all these algorithm classes derive from an interface which in addition to "execute" to launch the algorithm, a method called "isSupported" which takes as a parameter an enumerator defined in your Factory. This enumerator would list prerequisites which must be present or must hold true. For example, if you wanted the process to run asynchronously, then Asynchronous would be one such enumerator. Each implementation would then return true only if it is supported.

I would create another method "ratePriority" which takes as a parameter an enumerator defined in your factory which determines algorithm priority and returns a number from 0 to 10 (10 being best). By priority, I mean, if we were talking about sorting a list, priorities for me would be, WorksBestAlreadySorted or WorksBestRandom or RequiresLittleMemory, etc.

When you call the factory, you pass an array of supporting features and an array of priorities. Your factory would then, one by one, eliminate all algorithms which do not support all your supporting features. Assuming you have more than one algorithm left, you then determine the best one by calculating each algorithm's score by calling ratePriority on each priority and you select the highest.

In this way, the caller only has to worry about supporting features and priorities of each algorithm and not on how the actual implementation is performed. Rather than return an instance to the implementation, you could directly call execute on that implementation and return the results to that call.

I hope that helps!

Edit: If parameters to these algorithms change, you could accept Key-Value pair mappings that would then be passed onto the algorithm itself. The factory wouldn't have to worry about how to call it. The caller still would, however, I don't think there is a way of getting around this if those parameters are what is required.

share|improve this answer
    
Yes, this is actually helpful! –  user40989 Feb 4 at 11:33

Assuming the high-cost method has the same signature as the low-cost method. It should be possible to introduce a "preferedMethod()" which also has the before mentioned signature and is in every class implemented to delegate to the low-cost method.

void lowCostMethod( args ...){
    //Implementation
}

void highCostMethod( args ...){
    //Implementation
}

void preferedMethod( args ...){
   lowCostMethod(args)
}
share|improve this answer
    
Assuming the high-cost method has the same signature as the low-cost method. This assertion does not hold, as suggested by the I/O device example. –  user40989 Feb 3 at 12:50

Just add a method to the base class to get the preferred transport method.

TransportEnum getPreferredTransport()

Each subclass could return whatever it is and since the base class already knows all of the possible transports, since it provides a different interface for each of them you, aren't hurting the abstraction any further.

share|improve this answer

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.