Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // Deps1.playground
- import UIKit
- // Design issues / decisions:
- //
- // - should "providing" protocols use funcs or vars?
- // - i.e. `var serviceA` vs `func serviceA()`
- //
- // - for deps which themselves have deps ("internal nodes of the deps tree"),
- // should they take a deps object or should we ban them from using the deps pattern?
- // - i.e. if C depends on A and B, should we `class ServiceC { init(deps: Deps) }`
- // or should we `class ServiceC { init(a: ServiceA, b: ServiceB) }`?
- //
- // - if internal nodes take a deps object, the deps object will run into a bootstrapping problem
- // (`ServiceC.init()` needs a `Deps` and `Deps.init()` needs a `ServiceC`)
- // - we can get around this by using force-unwrapped vars (and a static builder method)
- // - or avoid the issue by banning internal nodes from using the deps pattern.
- //
- // - if internal nodes are banned from the deps pattern, we lose the ability to replace
- // a dep at run-time and have every part of the system automatically start using the new
- // dep (if ServiceC uses ServiceA (given via init), replacing Deps.serviceA
- // at runtime with a fake does not affect ServiceC, which will still use the original A).
- // This playground explores:
- // - "providing" protocols use vars
- // - "internal" nodes use deps pattern
- // - use force-unwrap to work-around the bootstrapping problem
- // Ultimately, it looks like this approach will not work because `var foo: Foo!`
- // does not satisfy a `var foo: Foo { get }` protocol.
- // service data types
- public typealias A = Int
- public typealias B = Int
- public typealias C = (A,B)
- // service protocols
- public protocol AServicing {
- func getAs() -> [A]
- }
- public protocol BServicing {
- func getBs() -> [B]
- }
- public protocol CServicing {
- func getCs() -> [C]
- }
- // service implementations
- public class AService: AServicing {
- public func getAs() -> [A] {
- return [1,2,3]
- }
- }
- public class BService: BServicing {
- public func getBs() -> [B] {
- return [11,12,13]
- }
- }
- public class CService: CServicing {
- public init(deps: AServiceProviding & BServiceProviding) {
- _deps = deps
- }
- public func getCs() -> [C] {
- return Array(zip(_deps.aService.getAs(), _deps.bService.getBs()))
- }
- private let _deps: AServiceProviding & BServiceProviding
- }
- // deps protocols
- public protocol AServiceProviding {
- var aService: AServicing { get }
- }
- public protocol BServiceProviding {
- var bService: BServicing { get }
- }
- public protocol CServiceProviding {
- var cService: CServicing { get }
- }
- // deps implementation
- public class Deps: AServiceProviding, BServiceProviding, CServiceProviding {
- public var aService: AServicing
- public var bService: BServicing
- // this fails to compile because the force-unwrapped version apparently doesn't satisfy the protocol requirement:
- public var cService: CServicing!
- public init(aService: AServicing, bService: BServicing) {
- self.aService = aService
- self.bService = bService
- }
- public static func makeDeps(
- aService: AServicing,
- bService: BServicing,
- cService: CServicing? = nil
- ) -> Deps {
- let deps = Deps(aService: aService, bService: bService)
- deps.cService = cService ?? CService(deps: deps)
- return deps
- }
- }
- // Some objects which use the deps:
- public class ControllerAB {
- public let deps: AServiceProviding & BServiceProviding
- public init(deps: AServiceProviding & BServiceProviding) {
- self.deps = deps
- }
- public func total() -> Int {
- return (deps.aService.getAs() + deps.bService.getBs())
- .reduce(0, { $0 + $1 })
- }
- }
- public class ControllerC {
- public let deps: CServiceProviding
- public init(deps: CServiceProviding) {
- self.deps = deps
- }
- public func total() -> Int {
- return deps.cService.getCs()
- .reduce(0, { $0 + $1.0 + $1.1 })
- }
- }
- // use real services:
- let a = AService()
- a.getAs() // [1,2,3]
- let b = BService()
- b.getBs() // [11,12,13]
- let deps1 = Deps.makeDeps(aService: a, bService: b)
- let c = CService(deps: deps1)
- c.getCs() // [(1,11),(2,12),(3,13)]
- deps1.cService.getCs() // [(1,11),(2,12),(3,13)]
- let controllerab1 = ControllerAB(deps: deps1)
- controllerab1.total() // 42
- let controllerc1 = ControllerC(deps: deps1)
- controllerc1.total() // 42
- // fake A and B implementations:
- public class FakeAService: AServicing {
- public func getAs() -> [A] {
- return [-1,-2,-3]
- }
- }
- public class FakeBService: BServicing {
- public func getBs() -> [B] {
- return [-11,-12,-13]
- }
- }
- // use fake A with real C:
- let fa = FakeAService()
- fa.getAs() // [-1,-2,-3]
- let deps2 = Deps.makeDeps(aService: fa, bService: b)
- let c2 = CService(deps: deps2)
- c2.getCs() // [(-1,11),(-2,12),(-3,13)]
- deps2.cService.getCs() // [(-1,11),(-2,12),(-3,13)]
- let controllerab2 = ControllerAB(deps: deps2)
- controllerab2.total() // 30
- let controllerc2 = ControllerC(deps: deps2)
- controllerc2.total() // 30
- // fake B with real C:
- let fb = FakeBService()
- fb.getBs() // [-11,-12,-13]
- let deps3 = Deps.makeDeps(aService: a, bService: fb)
- let c3 = CService(deps: deps3)
- c3.getCs() // [(1,-11),(2,-12),(3,-13)]
- deps3.cService.getCs() // [(1,-11),(2,-12),(3,-13)]
- let controller3 = ControllerAB(deps: deps3)
- controller3.total() // -30
- let controllerc3 = ControllerC(deps: deps3)
- controllerc3.total() // -30
- // fake A and fake B with real C:
- let deps4 = Deps.makeDeps(aService: fa, bService: fb)
- let c4 = CService(deps: deps3)
- c4.getCs() // [(-1,-11),(-2,-12),(-3,-13)]
- deps4.cService.getCs() // [(-1,-11),(-2,-12),(-3,-13)]
- let controllerab4 = ControllerAB(deps: deps4)
- controllerab4.total() // -42
- let controllerc4 = ControllerC(deps: deps4)
- controllerc4.total() // -42
- // or use a fake C directly:
- public class FakeCService: CServicing {
- public func getCs() -> [C] {
- return [(0,0)]
- }
- }
- let fc = FakeCService()
- fc.getCs() // [(0,0)]
- let deps5 = Deps.makeDeps(aService: a, bService: b)
- deps5.cService.getCs() // [(1,11),(2,12),(3,13)]
- deps5.cService = fc
- deps5.cService.getCs() // [(0,0)]
- let controllerab5 = ControllerAB(deps: deps5)
- controllerab5.total() // 42
- let controllerc5 = ControllerC(deps: deps5)
- controllerc5.total() // 0
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement