Swift의 프로토콜 이해.
내 마지막 게시물 이후 너무 오래되었습니다. 매체에서 나를 팔로우하는 사람들에게 좀 미안합니다. ㅋㅋㅋ. 마지막 게시물 이후로 많이 향상되었습니다. 돌이켜 보면 당시에 작성한 많은 코드와 개념이 매우 쓰레기처럼 보입니다. 하하. 어쨌든 이전보다 훨씬 더 많이 알게 된 것 같아서 그 과정에서 배운 것을 공유하기로 합니다. 그리고 그것이 당신에게 도움이 될 수 있기를 바랍니다.
이 기사에서는 Swift에서 프로토콜을 사용하는 방법에 대해 설명합니다 . 유사한 개념을 다른 OOP 프로그래밍 언어에 적용할 수 있습니다.
1. 프로토콜 준수
기본적인 것부터 시작하세요. 프로토콜을 준수하는 모든 개체는 해당 속성/메소드를 구현해야 합니다.
protocol Example {
func A()
}
struct AObject: Example {
// Compiler will ask you to implement Exmample protocol's function
// Type 'AObject' does not conform to protcol 'Example'
func A() {}
}
꽤 자명하다. 프로토콜은 " 추상화 "입니다. 객체가 어떻게 생겼는지 보여줍니다. 그러나 실제 구현은 개체 자체에 따라 다릅니다.
프로토콜은 또한 시스템을 보다 유연하게 만듭니다.
class HTTPService {
func sendRequest() {
// do API request
}
}
class ViewController {
let service: HTTPService
init(service: HTTPService) {
self.service = service
service.sendRequest()
}
}
DIP(Dependency Inversion Principle)는 상위 수준 모듈이 하위 수준 모듈에 의존해서는 안 된다고 명시합니다. 둘 다 추상화에 의존해야 합니다 .
protocol HTTPService {
func sendRequest()
}
class ProductionAPIService: HTTPService {
func sendRequest() {
// do API request
}
}
class StubTestingService: HTTPService {
func sendRequest() {
// For testing
}
}
class ViewController {
let service: HTTPService
init(service: HTTPService) {
self.service = service
service.sendRequest()
}
}
3. 프로토콜은 인터페이스다!!!!
제 생각에는 이것이 아마도 가장 중요한 개념일 것입니다. 많은 초보자가 이전 개념에 익숙하지만 프로토콜에 무엇을 작성해야 하는지 모릅니다.
운전자라고 상상해보십시오. 인터페이스는 브레이크, 가속 페달 및 스티어링 휠이어야합니다 (자동 운전 방법 만 알고 있으므로 레버에 대해 묻지 마십시오). 이 인터페이스는 프로토콜입니다. 후드 아래에서 일어난 일(문자 그대로)은 운전자에게 중요하지 않습니다. 드라이버는 인터페이스에만 노출되어야 합니다.
protocol AutomaticCar {
func stepOnGas()
func hitTheBreak()
func steerTheWheel()
}
class Driver {
let car: AutomaticCar
init(car: AutomaticCar) {
self.car = car
}
func goDriving() {
// The driver should only be exposed to the interface and nothing else.
car.stepOnGas()
car.steerTheWheel()
car.hitTheBreak()
}
}
protocol Loader {
func load()
}
class RemoteSource: Loader {
func load() {
// Do remotely load
}
private func doOtherOperation() {}
}
class LocalSource: Loader {
func load() {
// Do locally load
}
private func doSomethingElse() {}
}
class ViewController: UIViewController {
let service: Loader
init(service: Loader) {
self.service = service
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
service.load()
}
}
이것은 직장에서 나에게 많이 일어났습니다. 우리 회사의 레거시 코드베이스에는 위임을 사용하는 많은 사용자 지정 보기가 있습니다. 다음과 같이 보입니다.
protocol CustomViewDelegate {
func doA()
func doB()
func doC()
func doD()
}
// A customView with delegation that can be used to notify or pass data
class CustomView: UIView {
var delegate: CustomViewDelegate?
}
// VieweController has to conform to CustomViewDelegate becuase
// when we write "customView.delegate = self"
// we appoint the delegation to this ViewController
class ViewController: UIViewController, CustomViewDelegate {
let customView = CustomView()
init(){
super.init(nibName: nil, bundle: nil)
customView.delegate = self
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// And hence, we are forced to implement all these methods
func doA() {
<#code#>
}
func doB() {
<#code#>
}
func doC() {
<#code#>
}
func doD() {
<#code#>
}
}
그러나 이들은 모두 선택 사항입니다. 컴파일러는 이러한 메서드를 구현하도록 강요하지 않습니다. 이 "선택적 프로토콜 방법"을 달성하기 위해 프로토콜에 대한 확장을 작성할 수 있습니다.
extension CustomViewDelegate {
func doA() {}
func doB() {}
}
// Now that doA & doB have a defualt implemntation in the extension, we no longer need to implement these two. (You still can if needed.)
프로토콜에 두 개 이상의 메서드(또는 속성)가 있는 경우. 냄새야. Liskov 대체 원칙 및 인터페이스 분리 원칙을 위반했을 수 있습니다 . 예를 들어 보겠습니다.
protocol Developer {
func writeCode()
func beHappy()
}
struct FrontEndDev: Developer {
func writeCode() {}
func beHappy() {}
}
struct BackEndDev: Developer {
func writeCode() {}
func beHappy() {
fatalError("I'm not happy")
}
}
인터페이스 분리 원칙: 어떤 코드도 사용하지 않는 메서드에 강제로 종속되어서는 안 됩니다.
class Company {
let devs: Developer
init(devs: Developer) {
self.devs = devs
}
}
let companyA = Company(devs: BackEndDev())
companyA.devs.beHappy() // Leads to crash. The BackEndDev can't beHappy()
Liskov 대체 원칙: 상위 클래스의 개체는 응용 프로그램을 중단하지 않고 해당 하위 클래스의 개체로 교체할 수 있어야 합니다.
모든 것을 말하면서 메소드 또는 속성이 필요한지 확인하십시오.
6. 프로토콜은 다른 프로토콜을 따를 수 있습니다.
protocol BaseProtocol {
func cancel()
}
protocol SpecialTask: BaseProtocol {
func stopOperation()
func pauseOperation()
func startOperation()
}
struct SpecialForce: SpecialTask {
func stopOperation() {
<#code#>
}
func pauseOperation() {
<#code#>
}
func startOperation() {
<#code#>
}
func cancel() {
<#code#>
}
}
요청을 취소할 수 있어야 하는 API 요청에 대해 이와 같은 일부 구현을 보았습니다. (이 경우 자신의 프로토콜을 다른 프로토콜에 맞추는 대신 무료로 취소할 수 있는 Combine 프레임워크를 사용하는 것이 좋습니다 .)
7. 일반 프로토콜
개발자로서 우리는 항상 코드를 재사용할 수 있도록 노력합니다. 프로토콜도 예외는 아닙니다. 우리는 protocol generic을 만들기 위해 associatedType을 사용할 수 있습니다 .
// The Loader protocol doesn't know about the type that load function is going to complete with.
protocol Loader {
associatedtype Item
func load(completion: @escaping (Item) -> Void)
}
// Specify the type in implementation
class Service: Loader {
typealias Item = String
func load(completion: @escaping (Item) -> Void) {
}
}
// Or, you can just specify the type in the implemented function
class ServiceA: Loader {
func load(completion: @escaping (String) -> Void) {
}
}
모두 좋은 하루 보내시기 바랍니다. 앞으로 그리고 위로. 갑시다.

![연결된 목록이란 무엇입니까? [1 부]](https://post.nghiatu.com/assets/images/m/max/724/1*Xokk6XOjWyIGCBujkJsCzQ.jpeg)



































