Notice
Recent Posts
Recent Comments
Link
관리 메뉴

김종권의 iOS 앱 개발 알아가기

[iOS - swift] Protocol 지향 프로그래밍 (상속보다 프로토콜을 사용하는게 좋은 이유, 인터페이스, DIP, SRP) 본문

Refactoring (리펙토링)

[iOS - swift] Protocol 지향 프로그래밍 (상속보다 프로토콜을 사용하는게 좋은 이유, 인터페이스, DIP, SRP)

jake-kim 2023. 5. 21. 23:59

Protocol 지향 프로그래밍이란?

  • 특정 기능이 필요하여 기능을 구현하려고 할 때, protocol을 먼저 선언해 놓고 그 protocol을 준수하는 구현체를 생성하여 사용
  • 기존 기능을 그대로 사용하면서 새로운 기능을 추가하려고 할 때, 상속보다는 protocol을 사용하여 확장하는 형태로 구현하는 것

Protocol 지향 프로그래밍을 해야하는 이유

  • DIP (Dependency Inversion Principle): 소스코드 의존성이 구현체에 의존하지 않고 추상(protocol)에 의존하는 것
  • 기능제공(=확장성)
    • 확장성이라는 의미는 개발자가 코드를 작성할 때 매우 자연스럽게 사용이 가능
    • 사용하는쪽에서 매우 자연스러움 -> 프로토콜을 준수한다 -> 기능을 준수한다
  • protocol을 적절히 이용하면 코드를 사용하는쪽에서 관심사의 범위를 좁혀서 의존성을 낮추기가 가능 (ISP 원칙 - 이전 포스팅 글 참고)
  • 상속을 사용할때보다 더욱 변경에 유리한 코드 (아래에서 계속 이해하기)

protocol과 상속 차이 이해하기

  • 프로토콜은 추상적인 object의 행위를 정의하고 상속은 구현체의 object 자체를 정의
    • 특정 기능을 변경했을때 사이드이펙이 적음 - 상속을 사용하면 변경할 때 object 자체를 변경하는 것과 같고, 프로토콜을 변경하면 object의 행위만을 변경하는 것
  • 서브 클래스가 수퍼 클래스의 구현 세부사항에 결합되게 되어 수퍼 클래스의 변경이 서브 클래스에 영향을 미칠 수 있음
    • 개발자가 수퍼 클래스를 변경하려고 할 때 서브 클래스를 고려해야하는 상황이 발생
  • protocol을 이용했을 때, 해당 클래스를 변경하려고 하는 경우 다른 클래스를 고려하지않고 해당 클래스만 고려하여 수정하면 되므로 변경이 더욱 쉬움 (SRP 원칙)

프로토콜로의 리펙토링

  • 상속 -> 프로토콜로 변경
  • 기능 제공 -> 프로토콜로 만들어서 구현하고, 이를 준수하여 사용하도록 변경

ex) 상속인 경우 - SRP 위반

  • Shape클래스만 관심있지만 다른 클래스도 신경써야하는 상황
    • 프로토콜은 추상적인 object의 행위를 정의하고 상속은 구현체의 object 자체를 정의하기 때문에, Shape클래스를 변경한다는 것은 object를 변경한다는 것이고 이것은 서브클래스에도 영향을 미침
    • Shape에서 draw() 메소드의 이름을 변경하면 서브 클래스들에서 컴파일 에러 발생 
    • Shape에서 abc()라는 메소드를 추가하면, Circle에서 컴파일 에러가 발생
class Shape {
    func draw() {
        print("draw shape")
    }
}

class Circle: Shape {
    override func draw() {
        print("draw circle")
    }
    
    func abc() {
        print("abc")
    }
}

class Square: Shape {
    override func draw() {
        print("draw square")
    }
}

class Triangle: Shape {
    override func draw() {
        print("draw triangle")
    }
}

(위 코드를 protocol로 리펙토링)

  • 위에서 Shape는 object 자체이므로 변경될 여지가 많았지만, Drawable이라는 프로토콜로 변경됨으로서 새로운 기능을 추가할 땐 또 다른 protocol을 만들어서 준수하게하는 형태가 되므로 나중에 추가하기 쉽고, 수정하기 쉬운 코드가 유지됨
protocol Drawable {
    func draw()
}

class Circle: Drawable {
    func draw() {
        print("Drawing a circle")
    }
}

class Square: Drawable {
    func draw() {
        print("Drawing a square")
    }
}

class Triangle: Drawable {
    func draw() {
        print("Drawing a triangle")
    }
}

* 참고

https://ios-development.tistory.com/583

https://ios-development.tistory.com/572

Comments