Keyboard Shortcuts
Likes
Search
What OO feature would you call Swift’s Extension
I’ve been pondering for a few days about this. Asked a knowledgeable colleague and still wondering.?
I’m thinking it is a Open/Closed abiding Inheritance- What do you think? |
||
开云体育My favorite use of Swift extensions is wrapping a seam around an existing object:- Take a type. Any type. I don't have to own it. - Define a new Protocol which is a slice of that class (ISP) - Using an extension, declare that the type (which I may not own) conforms to the protocol (which I do own). - No implementation necessary because it already conforms to the slice of its own interface. - I can now use the Protocol instead of the type.
|
||
开云体育Don’t follow you Jon. ?Maybe an example would help. I’m just a simple caveman.?David Koontz ?? ? Email: ?david@... ?? ? (360) 259-8380 ? ? ?http://about.me/davidakoontz On Feb 24, 2021, at 9:52 PM, Jon Reid <jon@...> wrote:
|
||
开云体育Let's say you have code that uses UserDefaults (a persistent key-value store available in Foundation for all Apple platforms). Specifically, it calls the method for setting a persistent integer. Persistence, ugh. This is making the code untestable.We'd like to make it possible to use a Test Double instead. We can start by defining an empty protocol. I'll be lazy about naming it. protocol UserDefaultsProtocol { } Using an extension, declare that UserDefaults (defined by Apple) conforms to our new protocol: extension UserDefaults: UserDefaultsProtocol {} The compiler is happy so far. Then we can go into the declaration of UserDefaults, find the method we want among the many it defines: open func set(_ value: Int, forKey defaultName: String) Copy the entire line, paste it into UserDefaultsProtocol, and remove the open: protocol UserDefaultsProtocol { ? ? func set(_ value: Int, forKey defaultName: String) } The extension declares that UserDefaults conforms to this protocol. And it already declares this method. So it does conform, without adding anything to the body of the extension! The compiler is still happy. Now instead of directly accessing the UserDefaults.standard singleton, we declare a property to act as our seam: var userDefaults: UserDefaultsProtocol = UserDefaults.standard By default, the property is the actual UserDefaults.standard singleton unless we say otherwise. (Normally, I'd make this an immutable `let` property, passing it in via initializer with the default value.) Now instead of UserDefaults.standard.set(42, forKey: "meaning") we can change that to userDefaults.set(42, forKey: "meaning") to reference the property instead of the singleton. A manual test confirms this refactoring. And now we have testable code, because we can supply a Test Double that also conforms to UserDefaultsProtocol. Woot! …So this example isn't Open/Closed for tacking on an additional function. Instead, it's Open/Closed for conforming to a new interface?where that interface is a slice of the original.
|
||
'Seam' here refers to a concept popularised by Michael Feathers' book
toggle quoted message
Show quoted text
"Working effectively with legacy code" There's some good info here: Fox --- On Thu, 25 Feb 2021 at 14:28, David Koontz <david@...> wrote:
|
||
开云体育David,I think of the "seam" metaphor as "a place where two pieces are loosely stitched together, so we can safely connect different pieces." But Michael Feathers is more precise:
It gives us a place to break dependencies. - Jon
|
||
perhaps I am parsing your statement poorly Jon. open?func?set(_?value:?Int, forKey defaultName:?String) ? Back to the bigger picture - I think we are both saying that the Extension capability in Swift is an implementation of OO inheritance that conforms to the Open/Closed (SOLID) principle. |