[在一个非常小的游戏中,我写的player
有一些现金(只是Int
),但是我不想直接操纵player
类中的现金;我觉得玩家阶层不应该在乎现金操纵。
因此,我将所有现金处理都转移到另一个类中。
在这里,我感到困惑。我正在阅读VIPER和其他模式,其中“围墙花园”意味着一个班级通常将不了解其父级或直接访问另一个对象。
因此,在这种情况下,我不确定是否要把玩家的现金与将现金贷记/借记到玩家的钱包之间的担忧分开。
[在我的现金处理器中,我需要进行一些错误检查,throw
错误和其他操作;目前,这对我的问题并不重要;因此,我删除了这些内容,以专注于我的问题的主旨。
我正在使用Swift Playgrounds来探讨有关如何解决这个小问题的2个主要想法。
// : Idea #1 -- Use static functions
class Player {
var cash: Int = 0
}
let p = Player.init()
struct Wallet{
static func credit(account: Player, amount: Int) {
// (#TODO) the final one will do some checks, throw an error
var balance: Int = account.cash
balance += amount
account.cash = balance
}
static func debit(account: Player, amount: Int) {
// (#TODO) the final one will do some checks, throw an error
var balance: Int = account.cash
balance -= amount
account.cash = balance
}
}
Wallet.credit(account: p, amount: 125)
Wallet.debit(account: p, amount: 25)
print (p.cash)
好吧,在这一章中,我使用静态函数;但是Wallet
结构可以直接访问播放器。我觉得这是错的。
我第二次尝试:
class Player {
var account: Account = Account()
var cash: Int {
return account.balance
}
init(cash: Int) {
self.account = Account(openingBalance: cash)
}
}
var p = Player.init(cash: 50)
class Account {
public private(set) var balance: Int = 0
init(openingBalance: Int = 0) {
self.balance = openingBalance
}
func credit(amount: Int) -> Int {
balance += amount
return self.balance
}
func deposit(amount: Int) -> Int {
balance -= amount
return self.balance
}
}
p.account.credit(amount: 100)
print (p.cash)
这个人感觉更干净,但是现在Player
对象可以直接访问Account
?
编辑:我有第三次尝试。我看到了一个称为proxy design pattern
的东西,如果我不熟悉该模式,对不起。据我了解,您可以在玩家和银行帐户之间充当代理人的角色,以决定玩家是否可以贷记或借记他们的帐户。
可悲的是,该实验无法正常进行;现在,我有一个看似无止尽的do-catch语句循环;我不确定如何将其踢回主程序。
// : Third attempt -- I think this follows a Proxy pattern
public enum CashError: Error, Equatable {
case mustBePositive
case notEnoughFunds
case cannotPerformTransaction
case amountWouldBeNegative
}
class Bank {
var balance: Int = 0
enum TransactionType: Int {
case credit = 0
case debit
}
func performTransaction(transactionType: TransactionType, amount: Int) {
switch transactionType {
case .credit:
self.credit(amount: amount)
case .debit:
self.debit(amount: amount)
}
}
private func credit(amount: Int = 0) {
print ("didCredit: \(amount)")
self.balance += amount
}
private func debit(amount: Int = 0) {
print ("didDebit: \(amount)")
self.balance -= amount
}
}
class Customer {
private(set) var accountProxy: AccountProxy?
var cash: Int {
guard let proxy: AccountProxy = accountProxy else {
return 0
}
return proxy.balance
}
init(cash: Int = 0) {
print ("Create player with $\(cash)")
self.accountProxy = AccountProxy.init(customer: self)
guard let proxy = self.accountProxy else {
return
}
do {
let _ = try proxy.handle(transactionType: .credit, amount: cash)
} catch {
print (error)
}
}
}
class AccountProxy {
private var bank: Bank = Bank()
private var customer: Customer
public var balance: Int {
return self.bank.balance
}
init(customer: Customer) {
self.customer = customer
}
func handle(transactionType: Bank.TransactionType, amount: Int = 0) throws -> Bool {
print ("Attempting \(transactionType) of $\(amount)")
do {
if let _ = try canPerformTransaction(transactionType: transactionType, amount: amount) {
print ("proxy: says you can \(transactionType): $\(amount)")
self.bank.performTransaction(transactionType: transactionType, amount: amount)
return true
}
else {
print ("proxy: error - Cannot perform transction")
throw CashError.cannotPerformTransaction
}
} catch {
throw (error)
}
}
// (Private) functions
private func canPerformTransaction(transactionType: Bank.TransactionType, amount: Int ) throws -> Bool? {
switch transactionType {
case .credit:
do {
guard let result = try canCredit(amount: amount) else {
return false
}
return result
} catch {
throw error
}
case .debit:
do {
guard let result = try canDebit(amount: amount) else {
return false
}
return result
} catch {
throw error
}
}
}
private func canCredit(amount: Int) throws -> Bool? {
guard amount >= 0 else {
throw CashError.mustBePositive
}
return true
}
private func canDebit(amount: Int) throws -> Bool? {
// amount must be > 0
guard amount > 0 else {
throw CashError.mustBePositive
}
// balance must be >= amount
guard balance >= amount else {
throw CashError.notEnoughFunds
}
// the remaining sum must be >= 0
let sum = balance
guard ((sum - amount) >= 0) else {
throw CashError.amountWouldBeNegative
}
return true
}
}
let bob = Customer.init(cash: 100)
print ("Bob has $\(bob.cash)")
do {
let _ = try bob.accountProxy?.handle(transactionType: .credit, amount: 125)
} catch {
print (error)
}
print ("Bob has $\(bob.cash)")
do {
let _ = try bob.accountProxy?.handle(transactionType: .debit, amount: 25)
} catch {
print (error)
}
print ("Bob has $\(bob.cash)")
// (Logged Output):
// Create player with $100
// Attempting credit of $100
// proxy: says you can credit: $100
// didCredit: 100
// Bob has $100
// Attempting credit of $125
// proxy: says you can credit: $125
// didCredit: 125
// Bob has $225
// Attempting debit of $25
// proxy: says you can debit: $25
// didDebit: 25
// Bob has $200
因此,我的查询基于“围墙花园”的概念?玩家阶层应该了解其帐户吗?
我很抱歉,如果这看起来很明显,我会感到非常沮丧/困惑。
我对此表示感谢/感谢。
当然有这么小的例子,分离关注点可能不是很有用,但我会继续使用它...
class Player {
let wallet: Wallet
// no reference to cash here.
}
class Wallet {
private var cash: Int
// methods manipulating cash go here.
}
现在,播放器不需要任何代码来操纵cash
变量,但这意味着该播放器将必须公开其电子钱包,以便其他人可以投入现金或将其取出。这将关注点分开,但破坏了围堵。 Player对象无法再控制谁或什么从钱包中取出现金,并且在现实生活中,例如这些东西的例子,这听起来像是个坏主意。
您可以在Player中将电子钱包设为私有,但是这样做会使电子钱包多余。除非您不信任播放器以确保其cash
当然不会为负。
用例将决定可能的适当关系。我最喜欢的示例是关于汽车及其引擎的。对于“ startCar”用例,明显的关系是包含[Car]<>-->[Engine]
。驾驶员要发动汽车时,并不在乎引擎内部。但是,对于“换油”用例,最佳关系是关联[Car]--->[Engine]
,因为机械师将希望直接访问发动机,而不必每次将发动机从汽车中提起并更换它时,进行更改。
希望此杂乱无章...