0

I'm writing a library for use in scientific computing and ran into a bit of a quandary. The types at work here are a class M which consists of some data and a reference to a container class C. There are many different implementations of C and I devoted a lot of work to making sure that M objects could use C objects without knowing their internal representation.

The code may be used for high-performance scientific computing someday, so speed actually is a concern. If M were to break the encapsulation of C objects, the code could run faster. I tested this and indeed I could get a 50% speedup. But, that would involve lots of repeated code and violation of the open-closed principle.

Alternatively, I can take the behavior that M needs to perform and delegate it to C. By default, C will use the same implementation-agnostic algorithm that I had before, but the logic has just moved downtown to a new class. The advantage of this approach is that, if CO is an implementation of C which can do substantially better than the default implementation, it can override that method with its own version.

There is, at present, only 1 method that M will need to delegate to C. I can imagine at most 2 more behaviors that will need to be dealt with in this way. There may be a bit of repeated code, but it could be handled with a code generator too.

Is this a common approach? If so, what's it called? If not, is that because it's a terrible idea for some reason that I haven't noticed? It's not quite the strategy pattern; most of the container objects don't even bother implementing their own strategy, they use the default.

2
  • I think it's relevant to ask what language you're implementing this in.
    – Aaron Hall
    Jun 14, 2014 at 11:31
  • 50% faster is a deal-breaker : you may build a helper static class for repeating building blocks, but you can assume some copy-pasting. Just mature well a first implementation, then build the others from that point. Jul 14, 2014 at 22:15

1 Answer 1

1

Unless you have multilayered architecture and these classes fall in different layers, one way to think about this is in terms of responsibilities (given that OO aspects of your program are unclear to me from the question), instead of whether one class is "lower" or "higher". You would ideally want every class to have exactly one responsibility (which is the single responsibility principle) regardless of whether it exposes one method or many. In my experience, that goes a long way in achieving open-closed design, because the responsibility that you delegate to a class is well-contained within the class. As you extend your application, either you will have new classes with new responsibilities, or you will be extending existing classes if the functionality you are adding will fit in the existing classes' responsibilities.

Based on your description, whether the method is in M or in C, you can allow the derived class to override the method and improve the performance. And a derived class of M on a particular platform can also be one with the code that is targeted towards that platform instead of generic platform so that it improve the performance.

However, when you are thinking about generic implementation and platform specific optimizations, you do need to consider whether having them in a single class will add more than a single responsibility in one place. (And if this issue is not limited to a single class, then you may need to consider it at a higher level, e.g. see Separation of Concerns principle).

A second issue that comes into picture in this example may be whether M and C (if they needed to be two separate classes because of different responsibilities) is whether they need to be coupled (i.e. M needs to instantiate or directly use C or vice versa). In many cases that can be left up to the caller such that the caller would instantiate C or an implementation of C, and then pass it to M. In such a case, M only works against C (or an interface from which C is derived) and as you add more implementations of C, the calling code picks the correct one and then passes to M.

Wherever possible, I also look at other examples that may have already been implemented that either I know of from past projects, or that are publicly available, e.g. in .NET Framework Class Library (it doesn't necessarily have to be in the language you are using). FCL thus has a SymmetricAlgorithm class which is derived for every implementation of a symmetric cryptographic algorithm (such as AES), but if you needed to use a password for encryption, you would have to use a DeriveBytes class (which has its own derivations) to first compute a Key and an Initialization Vector, which are the input to the SymmetricAlgorithm. It is this separation that allows various types of key derivation algorithms to be targeted towards any type of symmetric algorithm.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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