1

我正在尝试为我的应用程序创建数据模型。这里是场景:将抽象类型转换为Swift中的混凝土类型

我的应用程序有客户模型,其中包含客户的信息,也包含他/她的支付来源。该API给我两种支付来源:银行帐户他们有完全不同的领域。

所以,这里是我的问题,我想要具有PaymentSource的抽象类型,然后在每个PaymentSource中都有一个函数来将对象转换为它的类型。一些我是如何删除类型的。

我需要把我的抽象类型放在一个盒子里,并用它作为具体类型(AnyPaymentSource)。

所以,我做了如下:

protocol PaymentSource { 
    associatedtype Kind 
    func cast() -> Kind 
} 

struct AnyPaymentSource<PS: PaymentSource> { 
    private var paymentSource: PS 
    init(paymentSource: PS) { 
     self.paymentSource = paymentSource 
    } 
    func cast() -> PS.Kind { 
     return paymentSource.cast() 
    } 
} 

struct Card: PaymentSource { 
    func cast() -> Card { 
     return self 
    } 
} 

struct BankAccount: PaymentSource { 
    func cast() -> BankAccount { 
     return self 
    } 
} 

struct Customer { 
    var firstName: String 
    var lastName: String 
    var email: String 
    var paymentSource : AnyPaymentSource<PaymentSource> 
} 

Customer给我的错误与下面的描述:

使用 'PaymentSource' 作为具体类型符合协议 'PaymentSource'不支持

我在哪里做错了?

+1

泛型类型“PS”必须是一个像“BankAccount”或“Card”这样的具体类型,而不是它符合的协议。 – vadian

+0

@vadian那么为什么还要为客户的PaymentSource创建抽象类型呢?我需要存储付款来源而不知道它是什么类型。然后在代码中获取类型 – Mohammadalijf

+0

这些'PaymentSource'结构的目的是什么?实际情况是,您最好为两种支付来源提供'enum'。 – XmasRights

回答

1

斯威夫特是statically typed language。这意味着必须在编译时知道变量的类型。

当我面对这个问题,我解决了这个问题是这样的

protocol PaymentSource { 
    associatedtype Kind 
    func cast() -> Kind 
} 

struct AnyPaymentSource<PS: PaymentSource> { 
    private var paymentSource: PS 
    init(paymentSource: PS) { 
     self.paymentSource = paymentSource 
    } 
    func cast() -> PS.Kind { 
     return paymentSource.cast() 
    } 
} 

struct Card: PaymentSource { 
    func cast() -> Card { 
     return self 
    } 
} 

struct BankAccount: PaymentSource { 
    func cast() -> BankAccount { 
     return self 
    } 
} 

struct Customer<T:PaymentSource> { 
    var firstName: String 
    var lastName: String 
    var email: String 
    var paymentSource : AnyPaymentSource<T> 
} 
func test(){ 
    let customerWithCard = Customer<Card>(
     firstName: "", 
     lastName: "", 
     email: "", 
     paymentSource: AnyPaymentSource(paymentSource: Card()) 
    ) 
    let customerWithBankAccount = Customer<BankAccount>(
     firstName: "", 
     lastName: "", 
     email: "", 
     paymentSource: AnyPaymentSource(paymentSource: BankAccount()) 
    ) 
    print(customerWithCard.paymentSource.cast()) 
    print(customerWithBankAccount.paymentSource.cast()) 
    return 
} 
+1

aaw谢谢你,我将在这个[博客]上混合你的答案和东西(https://www.bignerdranch.com/blog/breaking-down-type-erasures-in -swift /) – Mohammadalijf

+0

对我来说,我认为这里有一个“代码味道”。提到'cardCustomer = Customer (...'还有'AnyPaymentSource(paymentSource:Card())'似乎并不好,你在这里提到的是同样的东西 –

+0

你说的对,它可以做得更好。在这里没有考虑代码质量,只是试图展示我的想法 – Andrew

1

如果你有什么实现的是什么@Andrew Ashurov在他的回答中提到,没有必要实施AnyPaymentSource。正如Swift Protocols Documentation中提到的:

协议实际上并不实际实现任何功能。 尽管如此,您创建的任何协议都将成为完整的类型 用于您的代码

含义已经能够将协议当作类型

这可能是:

protocol PaymentSource { 
    func cast() -> Self 
} 

struct Card: PaymentSource { 
    func cast() -> Card { 
     return self 
    } 
} 

struct BankAccount: PaymentSource { 
    func cast() -> BankAccount { 
     return self 
    } 
} 

struct Customer { 
    var firstName: String 
    var lastName: String 
    var email: String 
    var paymentSource : PaymentSource? 
} 

创建客户:

let cardCustomer = Customer(firstName: "Card Fname", lastName: "Card Lname", email: "[email protected]", paymentSource: Card()) 

let bankAccountCustomer = Customer(firstName: "Bank Account Fname", lastName: "Bank Account Lname", email: "[email protected]", paymentSource: BankAccount()) 

注意,在Customer结构,PaymentSourcepaymentSource财产,这意味着它可以指派为符合PaymentSource协议(任何类型在你的情况下为CardBankAccount)。

+0

当你在客户的paymentSource上调用cast时,它会返回给你一个PaymentSource。with associatedtype我可以指定哪种类型它应该是cas t到。阅读此[博客](https://www.bignerdranch.com/blog/breaking-down-type-erasures-in-swift/)了解详细的问题和解决方案 – Mohammadalijf

相关问题