简体   繁体   English

将协议与关联对象绑定-使用Swift进行面向协议的编程

[英]Binding Protocols with Associated Objects - Protocol Oriented Programming with Swift

i'm an iOS dev with a couple of years of experience with swift, but rarely i've used PAT's... 我是一名iOS开发人员,具有几年的快速开发经验,但很少使用过PAT ...

This time, I was trying to move some code from an app that i've developed to a shared library that I use in a couple of projects. 这次,我试图将一些代码从我开发的应用程序转移到我在几个项目中使用的共享库。 The case is about a Factory that uses various Builders (that are decorators of my business resources) via an Abstract Builder protocol, to obtain Items (in the real case, ViewControllers). 该案例涉及一个Factory,该工厂通过Abstract Builder协议使用各种Builder(是我的业务资源的装饰器)来获取Items(在实际情况下是ViewControllers)。

The Builder relays upon some variables that the Factory passes to him, but those are at the application level, so, to extract this logic and put it into my library, i need to use a generic reference, and because I want to work in a Protocol Oriented Programming manner, it is an AssociatedType. Builder继承了Factory传递给他的一些变量,但是这些变量是在应用程序级别上的,因此,要提取此逻辑并将其放入我的库中,我需要使用泛型引用,并且因为我想在面向协议的编程方式,它是一个AssociatedType。

// The item that i want to receive from my factory
protocol Item { 
    var content: String { get }
}

// This is the Builder interface that the Factory consumes
protocol Builder {  

    // The Abstract Parameters that the Application should define
    associatedtype Parameters

    func build(_ parameters: Parameters) -> Item?
}

// The BusinessResource of my library 
protocol BusinessResource { }

// The Factory that consumes the Builders
protocol Factory {

    associatedtype FactoryBuilder: Builder

    var parameters: FactoryBuilder.Parameters { get }

    func make(from businessResource: BusinessResource) -> Item?
}

// The generic implementation of my Factory
extension Factory {

    func make(from businessResource: BusinessResource) -> Item? {

        guard let builder = businessResource as? FactoryBuilder else {
            return nil
        }
        return builder.build(self.parameters)
    }
}

At this point everything looks good. 至此,一切看起来都很好。

I have two protocols and those are binded together, sharing a common type who is generic (the Builder Parameters). 我有两个协议,它们被绑定在一起,共享一个通用的通用类型(Builder Parameters)。

So, on the application layer, now i could introduce my concrete Parameters (i'll call them ConcreteParameters XD) 因此,现在在应用程序层上,我可以介绍我的具体参数(我将它们称为ConcreteParameters XD)

// The concrete parameters of the Application Factory
struct ConcreteParameters {
    let string: String
}

// The Builder interface restricting Parameters to ConcreteParameters 
protocol BindedBuilder: Builder where Parameters == ConcreteParameters {

}

// The Factory interface restricting Parameters to ConcreteParameters 
protocol BindedFactory: AbstractFactory where FactoryParameters: ConcreteParameters {

}

So far, so good. 到现在为止还挺好。 Everything looks in place and I'm start thinking that this could work, so now i try to implement a concrete Factory on the application to try if this really works. 一切都准备就绪,我开始认为这可以工作,所以现在我尝试在应用程序上实现一个具体的Factory,以尝试是否真的可行。

// The concrete output of my Builder
struct ConcreteItem: Item {
    var content: String
}

// The concrete BusinessResource that i get from my library
struct ConcreteObject: BusinessResource {

    let string: String
}

// The decoration extension that makes ConcreteObject compliant with Builder
extension ConcreteObject: Builder {

    typealias Parameters = ConcreteParameters

    func build(_ parameters: ConcreteParameters) -> Item? {
        return ConcreteItem(content: parameters.string + self.string)
    }
}

// The real Factory inside my app
class ConcreteFactory: BindedFactory {

    typealias FactoryBuilder = BindedBuilder

    var parameters: ConcreteParameters {
        return ConcreteParameters(string: "Hello ")
    }
}

let item = ConcreteFactory().make(from: ConcreteObject(string: "world!"))
print(item ?? "NOT WORKING")

At this point something breaks... I get this error: 在这一点上,出现了一些问题...我得到了这个错误:

错误:类型ConcreteFactory不符合AbstractFactory

[ EDIT : Error came from a previous version of the snippet, AbstractFactori is current Factory ] [ 编辑 :错误来自代码段的先前版本, AbstractFactori是当前的Factory ]

It is a Bug?? 这是一个错误? I really don't know how to solve this... 我真的不知道该怎么解决...

I think in this case you need to use a concrete type to alias FactoryBuilder instead of BindedBuilder , as protocols do not conform to themselves . 我认为在这种情况下,您需要使用一种具体的类型来别名FactoryBuilder而不是BindedBuilder ,因为协议不符合它们自己

This code effectively compiles, would something like that match your requirements? 该代码可以有效地进行编译,类似这样的代码是否符合您的要求?

class ConcreteFactory: BindedFactory {
    typealias FactoryBuilder = ConcreteObject

    var parameters: ConcreteParameters {
        return ConcreteParameters(string: "Hello ")
    }
}

Otherwise you can also try type erasing BindedBuilder and create AnyBindedBuilder , as suggested in the same link. 否则,您也可以尝试键入擦除BindedBuilder并创建AnyBindedBuilder ,如同一链接中所建议。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM