Knowing when a customization point has been customized?

In objective-c, I would use optional protocol methods to provide customization points and then check to see if it was implemented:

if ([delegate respondsToSelector:"optionalMethod"]){
     //Use optionalMethod to do customized thing
}else{
     //Do a highly optimized thing!
}


This allowed me to have a highly optimized path, while still allowing customization...


In Swift, I am loving protocol extensions, but I can't figure out how to provide an optimized path for objects/structs/enums which don't override a particular customization point. Is there anyway to (efficiently) test if a customization point has been overriden?

Accepted Reply

If your frequencies are Doubles or Floats, you could return Float.NaN from the default implementation as an indicator and check for it with value.isNaN in the calling method.

Replies

Hmm... I just had the thought of marking the method as "throws", and then having the default implementation throw a particular error (and then using that to provide the optimized case). Any idea what type of performance the new error handling methods provide? (I know obj-c exceptions had poor performance, but swift may have a swiftier model...)

You mean the user should be able to perform some customization of something at runtime (dynamically)?

Oh, I mean that the programmer (user of my framework/code) should be able to customize its behavior by overriding a function/method (which has a default implementation in a protocol extension). If they override it, then they get customized behavior, but if they don't override it, I want to be able to use that knowledge (that they didn't override it) to provide a much more optimized algorithm.


As a theoretical example, if a tableView knew that all of its cells would be the same height, it could use a much quicker layout algorithm. But it could still provide a customization point (e.g. optional method) to provide different heights and switch to the slower algorithm if that actually gets implemented.

As a more concrete example from my code (I am running into this in several places):


In another thread, I was asking about having an enum which returns a random member with a given frequency. I can implement this as a protocol + protocol extension, so that any enum (with an Int RawValue) that I add conformance to gains this ability (without actually providing any additional methods)


If the enum provides a "frequency" method, it gets called once for each member in the enum. However, if the enum doesn't provide that method, I would like to use a much faster algorithm to just provide a random member with equal probability for each member.

I think the Swift 2 way would be to use protocol extensions to provide default implementations, possibly along with constraints on the extensions to customize them for specific conformer types.


The other possibility is to use stored optional closures instead of the delegate methods, which you can obviously test for nil. It might be a bit onerous to set these up, though.

I think this is on the right track. The question is: how do I know whether the default version from the protocol extension was used or not?


I am considering having the default implementation simply throw a specific error, but it feels like a bit of a hack.

If your frequencies are Doubles or Floats, you could return Float.NaN from the default implementation as an indicator and check for it with value.isNaN in the calling method.

Or use an optional for the return type, with no value set by the default implementation?

I know this is not the answer you want to hear, and I'm not established enough in Swift to claim a definitive point of view, but I'd suggest restraint. Protocol extensions seem to lead very quickly into very deep waters (I've seen several different scenarios arise just in the last couple of days), and I think it's mistake to over-generalize solutions. If it were me, I wouldn't expect to find the most satisfactory pattern the first time around.


So, my suggestion is to use protocol extensions to implement default behavior simply in order to get things functional without duplication of code, then evaluate the pattern that provides before you start worrying about optimizing certain paths.


Sorry, that's so generic as to be more or useless, I know, but you didn't give us a very specific scenario to work with. 🙂

That is very good advice actually.


In this case, it is mostly me groking around with different ideas on a non-critical project which I am using to learn (so that I can see what works and what doesn't for later critical/client projects). Dispite being non-critical, it is still a project which I still plan to release though, so restraint is still useful.


I think for this particular piece of code, I will use LCS's suggestion of returning NaN from the method (since it returns a double). I would ultimately like to find a more general solution (as it is something I have encountered somewhat frequently in ObjC land), but NaN will work in this particular case.