Search code examples
swiftdesign-patternsproxyprivate-members

Using the swift proxy pattern, how do you modify a private variable?


I am experimenting with a basic proxy pattern in Swift playgrounds; I am not 100% familiar with it; but some videos are helping.

In my experiment, the subject, C is called through a proxy to perform said actions

ie:

A - Proxy - C

However, in my experiments I find out I can just call C itself directly, without even needing the proxy; so I am left wondering why bother with a proxy when I can call C directly anyway?

So I decided to make the variables in C private; but now I can't call them, or make modifications, and I am not sure how to resolve it.

I also tried a "decorator" pattern I found on stackoverflow to try to manipulate Cs variables, but still its marked private, so its not possible.

Also, the decorator pattern I found uses static variables. This makes me think well, why not just write a static function that creates c in memory, and then update the reference in proxy to that of the results of the static function.

I guess what I'm mis-understanding here is what is the point of a proxy class if you can just call the subject (in this case C) directly anyway?

Examples use Swift playground:

class A {
    let proxy: Proxy = Proxy()
    var funds: Int {
        return self.proxy.funds
    }
}

class Proxy {
    private let c: C = C()
    public var funds: Int {
        return c.funds
    }

    private func canIGetFunds() -> Bool {
        guard (self.funds > 0) else {
            return false
        }
        return true
    }

    func subtractFunds(funds: Int = 0) {
        if (canIGetFunds()) {
            print ("you have enough funds")
            willDebit(funds: funds)
        }
        else {
            print ("Not enough funds")
        }
    }

    private func willDebit(funds: Int = 0) {
        self.c.funds -= funds
    }
}

class C {
    private (set) var funds: Int = 100
}

let a = A()
print (a.funds)
a.proxy.subtractFunds(funds: 50)

This code will throw up a compiler error on:

self.c.funds -= funds

because:

funds setter is inaccessible

the funds variable is indeed private.

So, I tried a decorator too; code I found from stackoverflow (its not mine):

@propertyWrapper
struct Announced<T, U> {

    private var announcedFunction: (T) -> U
    var wrappedValue: (T) -> U { announcedFunction }

    init(wrappedValue: @escaping (T) -> U) {
        announcedFunction = { args in
            let rv = wrappedValue(args)
            print("In: \(args)")
            print("Out: \(rv)")
            return rv
        }
    }
}

extension Wallet {
    @Announced static var add: ((Int, Int)) -> Int = { $0.0 + $0.1 }
    @Announced static var subtract: ((Int, Int)) -> Int = { $0.0 - $0.1 }
}

Now here the add and subtract are static variables; and the contents of the closure are of course wrong because I make no reference to the actual Wallet variables -- I've only put it like this to help with discussion.

The way I got around it is to try to make the (Int, Int) take in a Wallet class, and then return a modified Wallet object where the add or subtract has been completed

So something like this:

@Announced static var subtract: ((w:Wallet, Int)) -> Wallet = {
        let newWallet = Wallet()
        newWallet.cash = w.cash
        newWallet.cash -= $0.1
        return newWallet
    }

But if I do this pattern, I don't see why I don't just write a static function and disregard this decorator.

I've even tried making C follow a delegate pattern where the Wallet is a delegate of C and adds, subtracts funds; but this too has an issue in that you can call C directly and because is its own delegate it has no reason to fail or throw up an error, etc.

So anyway, probably I'm just overcomplicating:

Whats the point of having a proxy if you can just call the subject directly and do your manipulation that way?

Is there a way to make the proxy pattern the only way you can call and make actions on the subject?

With thanks?


Solution

  • The easiest way is using public/internal setter

    class C {
        var funds: Int = 100
    }
    

    But if you define C in this same file with Proxy then you can use fileprivate(set) access modifier

    class Proxy {
        ...
    }
    
    class C {
        fileprivate(set) var funds: Int = 100
    }