Many people have blogged on how to do dependency injection (DI), but few tell you why to use it.
What is dependency injection? It is the practice of passing objects that a class or method depends on, into the object or method, instead of the class or method creating that object itself.
Why use it? The purpose of dependency injection is to create a design for your code that is (a) testable, (b) separates responsibilities, and (c) is more flexible.
If your class or method creates objects and uses them, it could be argued that your code has too many responsibilities: creating objects is a difference responsibility than using them. That's a design argument for using DI.
If your class or method creates and uses objects it depends on, that makes unit testing it more difficult because your tests can't control or mock out those objects. This is the testing argument for using DI.
If your class or method creates and collaborates with objects it depends on, that makes re-using it to collaborate with different objects more difficult. This is the flexibility argument for using DI.
There's a nice description of using DI with Swift here: https://cocoacasts.com/nuts-and-bolts-of-dependency-injection-in-swift/
C. Keith Ray
C. Keith Ray writes about and develops software in multiple platforms and languages, including iOS® and Macintosh®.
Keith's Résumé (pdf)
Keith's Résumé (pdf)
Saturday, December 9, 2017
Friday, October 20, 2017
How can we create an operator to append a value to an array?
This is a sample from my book in progress at LeanPub: What Every Programmer Needs to Know About Swift.
Sometimes you want to be succinct and repetitive — as in appending values to an array. We could create an operator function that modifies the array, but that doesn’t let us use the operator multiple times in the same expression. This is because the temporary values you get in a complex expression are immutable.
By making the precedence of this operator less than one of the lowest precedences (TernaryPrecedence), we can mix other expressions and still get the results we expect. Though you should use parentheses to make complicated expressions more clear.
Sometimes you want to be succinct and repetitive — as in appending values to an array. We could create an operator function that modifies the array, but that doesn’t let us use the operator multiple times in the same expression. This is because the temporary values you get in a complex expression are immutable.
precedencegroup AppendPrecedence {
associativity: left
lowerThan: TernaryPrecedence
}
infix operator «: AppendPrecedence
func « <T>(lhs: Array<T>, rhs: T) -> Array<T>
{
var result = lhs
result.append(rhs)
return result
}
let x = [1,2,3,4]
let z1 = (x « 5) « 6
print(z1) // prints [1, 2, 3, 4, 5, 6]
let z2 = x « 5 « 6
print(z2) // prints [1, 2, 3, 4, 5, 6]
let z3 = x « 4 + 5 * 9 « 12
print(z3) // prints "[1, 2, 3, 4, 49, 12]"
let z4 = x « 4 < 5 ? 22 : 33 « 12
print(z4) // prints "[1, 2, 3, 4, 22, 12]"
By making the precedence of this operator less than one of the lowest precedences (TernaryPrecedence), we can mix other expressions and still get the results we expect. Though you should use parentheses to make complicated expressions more clear.
Tuesday, October 10, 2017
Playing With Compare And Closures
// playing with compare() and closures
import Foundation
print("actOn start")
let one = "1"
let two = "2"
func actOn(_ result: ComparisonResult,
ifLess: ()->Void,
ifEqual: ()->Void,
ifGreater: ()->Void ) {
switch result {
case .orderedAscending:
ifLess()
case .orderedSame:
ifEqual()
case .orderedDescending:
ifGreater()
}
}
actOn(one.compare(two), ifLess: {print(" string 1 < 2")},
ifEqual: {print(" string 1 == 2")},
ifGreater: {print(" string 1 > 2")})
actOn(two.compare(two), ifLess: {print(" string 2 < 2")},
ifEqual: {print(" string 2 == 2")},
ifGreater: {print(" string 2 > 2")})
actOn(two.compare(one), ifLess: {print(" string 2 < 1")},
ifEqual: {print(" string 2 == 1")},
ifGreater: {print(" string 2 > 1")})
print("actOn end\n")
print("switch start")
switch one.compare(two) {
case .orderedAscending: print(" switch 1 < 2")
case .orderedSame: print(" switch 1 == 2")
case .orderedDescending: print(" switch 1 > 2")
}
switch two.compare(two) {
case .orderedAscending:
print(" switch 2 < 2")
case .orderedSame:
print(" switch 2 == 2")
case .orderedDescending:
print(" switch 2 > 2")
}
switch two.compare(one) {
case .orderedAscending:
print(" switch 2 < 1")
case .orderedSame:
print(" switch 2 == 1")
case .orderedDescending:
print(" switch 2 > 1")
}
print("switch end\n")
print("compare start")
func compareComparable
>(_ expr1: T, _ expr2: T, ifLess: ()->Void, ifEqual: ()->Void, ifGreater: ()->Void ) {
if expr1 < expr2 {
ifLess()
}
else if expr1 == expr2 {
ifEqual()
}
else if expr1 > expr2 {
ifGreater()
}
else {
assertionFailure("\(expr1) isn't < or == or > \(expr2)")
}
}
compare(1, 2, ifLess: {print(" 1 < 2")},
ifEqual: {print(" 1 == 2")},
ifGreater: {print(" 1 > 2")})
compare(2, 2, ifLess: {print(" 2 < 2")},
ifEqual: {print(" 2 == 2")},
ifGreater: {print(" 2 > 2")})
compare(2, 1, ifLess: {print(" 2 < 1")},
ifEqual: {print(" 2 == 1")},
ifGreater: {print(" 2 > 1")})
// trigger assertion (as designed)
compare(Double.nan, Double.leastNonzeroMagnitude, ifLess: {print("NaN < n")},
ifEqual: {print(" NaN == n")},
ifGreater: {print(" NaN > n")})
print("compare end\n")
----------- output -------------------------------------------------------
actOn start
string 1 < 2
string 2 == 2
string 2 > 1
actOn end
switch start
switch 1 < 2
switch 2 == 2
switch 2 > 1
switch end
compare start
1 < 2
2 == 2
2 > 1
fatal error: nan isn't < or == or > 4.94065645841247e-324: file forloop.playground, line 76
Saturday, October 7, 2017
How Do I Use "Switch" to Initialize a Variable or Constant?
This is a sample from my book in progress at LeanPub: What Every Programmer Needs to Know About Swift.
You might want to be able to use 'switch' to initialize a variable or constant, given 3 or more possible conditions (if it's just two, you can use the trinary operator ":?
")
enum Color { // In this example, assume we can't associate
case red // values with these cases _in_ this enum, perhaps
case green // because we're not allowed to modify this code.
case blue
case purple
}
let c = Color.green
// a one-line "switch expression" which doesn't exist in Swift.
let menuNumber = switch c { case .red: 1, case .green: 2,
case .blue: 3, default: 0 } // ILLEGAL SYNTAX
You can use a switch statement or if/else statements to initialize the value; the compiler knows you are initializing a constant within that logic. As long as in every code path before the constant is used, you initialize the constant once and only once.
let menuNumber : Int
switch c {
case .red: menuNumber = 1
case .green: menuNumber = 2
case .blue: menuNumber = 3
default: menuNumber = 0
}
print(menuNumber) // prints 2
You could also create a dictionary and subscript it in the same line of code:
// moral equivalent of a one-line "switch expression":
let menuNumber = [Color.red : 1, Color.green : 2, Color.blue : 3][c] ?? 0
print(menuNumber) // prints 2
We need "?? 0" because subscripting a dictionary returns an optional, which will be nil if the subscript value isn't found in the dictionary. The "??" operator returns the unwrapped value of the optional, if the optional isn't nil, and it returns the right-hand value, 0
, if optional value is nil.
This is actually less code than "switch expression", but some people may find harder to read than the explicit logic of switch/if/else/etc. Use caution.
:?
")enum Color { // In this example, assume we can't associate
case red // values with these cases _in_ this enum, perhaps
case green // because we're not allowed to modify this code.
case blue
case purple
}
let c = Color.green
// a one-line "switch expression" which doesn't exist in Swift.
let menuNumber = switch c { case .red: 1, case .green: 2,
case .blue: 3, default: 0 } // ILLEGAL SYNTAX
let menuNumber : Int
switch c {
case .red: menuNumber = 1
case .green: menuNumber = 2
case .blue: menuNumber = 3
default: menuNumber = 0
}
print(menuNumber) // prints 2
// moral equivalent of a one-line "switch expression":
let menuNumber = [Color.red : 1, Color.green : 2, Color.blue : 3][c] ?? 0
print(menuNumber) // prints 2
0
, if optional value is nil.Wednesday, September 20, 2017
Is True Greater Than False in Swift 4?
This is a sample from my book in progress at LeanPub: What Every Programmer Needs to Know About Swift.
Is true > false?
Relational operators ("
<
", ">
", "<=
", ">=
") are not defined for Bool values. So "true > false
" is a syntax error.
However, if you want those operators to work with Bool values (so you can sort "
false
" before "true
", for example), you canmake Boolean conform to the "Comparable
" protocol. I'll illustrate:extension Bool : Comparable {
// "Is the left-hand-side value
// less than the right-hand-side value?"
public static func <(_ lhs: Bool, _ rhs: Bool)
-> Bool {
switch (lhs, rhs) {
case (false, true):
return true
default:
return false
}
}
}
assert( (true > false) == true )
assert( (true >= false) == true )
assert( (true < false) == false )
assert( (true <= false) == false )
assert( (false > true) == false )
assert( (false >= true) == false )
assert( (false < true) == true )
assert( (false <= true) == true )
The Comparable protocol requires two operators be implemented: "
<
" and "==
". It defines the other relational operators in terms of those two. I only need to implement "<
", since Bool already implements "==
".Tuesday, September 19, 2017
What Every Programmers Needs to Know About Swift
I have a book in progress at LeanPub titled What Every Programmer Needs to Know About Swift.
I've set the minimum price to $0.00 for a while, so buying the book now is at no risk for you. (Plus, LeanPub has a money-back guarantee. See also their Terms of Service.)
This book is intended for people who already know a programming language and are getting started using Swift. Or, you may already know Swift, but have questions. Or, like me, you keep forgetting the syntax and you’re tired of searching the web and having to carefully read the pages you find to figure out which version of the Swift language some example code was written in. Plus, the page you find on the web might be wrong or long-winded.
My book is written for Swift 4, and every line of code has been tested in a Swift playground in Xcode. You should be able to copy and paste my code confidently.
It's in a question-and-answer format inspired by C++ Faqs by Cline and Lomow. (There are more recent C++ Faqs books on Amazon, which, if they are as good as the original edition, you should read if you’re programming in C++.
I'm also going to publish snippets from the book on my blog.
If you buy the book, please join and participate on the mailing list! (See the Introduction in the book.)
Saturday, April 29, 2017
Implementing Control Structures in Swift
In Smalltalk, control structures like "for" or "while" were not part of the language, but built using the language itself. The Smalltalk compiler would special-case these for efficiency, but you could make your own, if you wanted to.
In Swift, you can almost do the same, though the syntax makes it look less like native syntax.
I just wrote this in a Swift playground in Xcode 8.1.
NOTE: just because this is possible, doesn't mean you should do this. Readability and testability matters. This is just exploring the language.
In Swift, you can almost do the same, though the syntax makes it look less like native syntax.
I just wrote this in a Swift playground in Xcode 8.1.
NOTE: just because this is possible, doesn't mean you should do this. Readability and testability matters. This is just exploring the language.
import Foundation
extension Int {
func through(_ end: Int, `do`: (_ i: Int)->() ) {
var i = self
while i <= end {
`do`(i)
i += 1
}
}
// originally, I thought of naming this 'to' but that's ambiguous
// whether the loop executes using the end value or not.
}
print("through loop 12-15")
12.through(15, do: {i in print("\(i)")})
/* prints
through loop 12-15
12
13
14
15
*/
print("through loop 1-5")
1.through(5) { index in print("\(index)") }
/* prints:
through loop 1-5
1
2
3
4
5
*/
extension Int {
func upTo(_ end: Int, `do`: (_ i: Int)->() ) {
var i = self
while i < end {
`do`(i)
i += 1
}
}
}
print("upTo 12-15")
12.upTo(15, do: {i in print("\(i)")})
/* prints
upTo 12-15
12
13
14
*/
print("upTo 0-5")
0.upTo(5) { index in print("\(index)") }
/* prints
upTo 0-5
0
1
2
3
4
*/
/*
It's much more readable if you use the built-in syntax:
*/
print("for 12 through 15")
for i in 12...15 {
print("\(i)")
}
/* prints
for 12 through 15
12
13
14
15
*/
print("for 0 up to 5")
for i in 0..<5 { print("\(i)") }
/* prints
for 0 up to 5
0
1
2
3
4
*/
Saturday, April 15, 2017
Template Design Pattern in Swift
In the Template Design pattern, you write an abstract class where an algorithm is implemented, but at least one part of the algorithm is not implemented -- it calls an abstract/unimplemented method for some part of the algorithm. The subclass inherits from the abstract class, implementing the abstract method(s) but not changing the overall algorithm.
I will show two ways to implement this design pattern.
First, using classes:
import Foundation
class TemplateClass { // "Abstract" class
func doit() {
before()
during()
after()
}
func before() {
print("before")
}
func during() { // in C++ should be "pure virtual".
fatalError("subclass must override")
} // in Java, should be "abstract"
func after() {
print("after")
}
}
class TemplateClassExample: TemplateClass {
override func during() {
print("during")
}
}
var tx = TemplateClassExample()
tx.doit() // prints "before", "during", "after"
Second, using Protocols:
protocol TemplateProtocol {
func during()
}
extension TemplateProtocol {
func doit() {
before()
during()
after()
}
func before() {
print("before")
}
func after() {
print("after")
}
}
class TemplateProtocolExample: TemplateProtocol {
internal func during() {
print("during")
}
}
var tp = TemplateProtocolExample()
tp.doit() // prints "before", "during", "after"
Here the protocol forces us to implement the abstract method, but we can't put the algorithm's concrete methods in the protocol declaration, they have to go in the protocol extension. This seems to be a rather arbitrary restriction, but it's probably reflecting the way Swift evolved. I don't think the earliest versions of Swift allowed extending protocols with extensions containing concrete methods, though obviously Swift 3.x does allow this.
Subscribe to:
Posts (Atom)