我正在尝试生成一个符合协议Protocoling
的ViewModel,该协议是通用的,并且具有关联的类型。
有一些符合协议的ViewModel,因此我正在尝试为viewModel创建一个工厂。
我已经通过Swift掩盖了以下错误:
Protocol can only be used as a generic constraint because it has Self or associated type requirements
示例代码:
protocol Protocoling {
associatedtype modulingType
var data: modulingType { get }
}
enum MyTypes {
case myName
case myAddress
}
class NameViewModel: Protocoling {
let data: String
init(name: String) {
data = name
}
}
class AddressViewModel: Protocoling {
let data: [String]
init(address: [String]) {
data = address
}
}
class DataFactory {
func viewModel(forType type: MyTypes) -> Protocoling {
switch type {
case .name: return NameViewModel(name: "Gil")
case .address: return AddressViewModel(address: ["Israel", "Tel Aviv"])
}
}
}
错误出现在func viewModel(forType type: MyTypes) -> Protocoling
中。
有没有办法解决这个问题?
答案 0 :(得分:0)
您可以使用具有关联类型(PAT)的协议作为返回类型,而无需更多约束,因为编译器需要知道要使用哪种类型。
在您的情况下,您必须使用一种称为类型擦除的技术才能使用任何 协议:
class AnyProtocoling: Protocoling {
let data: Any
init<U: Protocoling>(_ viewModel: U) {
self.data = viewModel.data as Any
}
}
class DataFactory {
func viewModel(forType type: MyTypes) -> AnyProtocoling {
switch type {
case .myName:
return AnyProtocoling(NameViewModel(name: "Gil"))
case .myAddress:
return AnyProtocoling(AddressViewModel(address: ["Israel", "Tel Aviv"]))
}
}
}
这将允许您“擦除”协议的关联类型并返回视图模型的Any
版本。
为了理解为什么PAT需要如此工作,我喜欢以下示例:Equatable
协议(它是PAT):
static func ==(lhs: Self, rhs: Self) -> Bool
此函数使用Self
类型,它是一个关联类型。您想在下一个通用函数中使用它:
func areEquals(left: Equatable, right: Equatable) -> Bool {
return left == right
}
此处,编译器将触发此错误:Protocol can only be used as a generic constraint because it has Self or associated type requirements
。为什么?让我们举个例子:
struct tomato: Equatable {}
struct salad: Equatable {}
areEquals(left: tomato(), right: salad())
没有理由比较西红柿和沙拉。关联的类型Self
不同。为了避免这种情况下的错误,您需要按以下方式约束Self
类型:
func areEquals<T: Equatable>(left: T, right: T) -> Bool
现在,您知道T
是相等的,并且具有相同的关联类型。
答案 1 :(得分:0)
这很容易修复,在具体的工厂实现中,您只需要为工厂指定必须符合协议protocoling
的泛型即可,请参见以下代码:
快捷键4
protocol Protocoling {
associatedtype modulingType
var data: modulingType { get }
}
enum MyTypes {
case myName
case myAddress
}
class NameViewModel: Protocoling {
let data: String
init(name: String) {
data = name
}
}
class AddressViewModel: Protocoling {
let data: [String]
init(address: [String]) {
data = address
}
}
class DataFactory<T> where T: Protocoling {
func viewModel(forType type: MyTypes) -> T? {
switch type {
case .myName: return NameViewModel(name: "Gil") as? T
case .myAddress: return AddressViewModel(address: ["Israel", "Tel Aviv"]) as? T
default: return nil /* SUPPORT EXTENSION WITHOUT BREAKING */
}
}
}
这是进入带有协议的奇妙抽象世界的第一步。您真的可以用它创造一些惊人的东西。虽然,我不得不说,就个人而言,它不像继承那样直观,但它是创建解耦和抽象系统的一个小难题,实际上更强大。
Swift是一种很棒的入门语言,我相信它的协议和扩展机制使其成为更复杂和有趣的语言之一。
这种设计模式是设置诸如依赖注入之类的好方法。