effective-swift

item64. 객체는 인터페이스를 사용해 참조하라

객체는 상위 타입의 객체를 사용해 참조하라

객체는 클래스가 아닌 프로토콜로 혹은 상위 타입의 객체를 이용하여 참조 하십시오. 적합한 프로토콜만 있다면 가능한 매개변수뿐 아니라 반환값, 변수, 필드를 전부 프로토콜 타입으로 선언하십시오. 객체의 실제 클래스를 사용해야 할 상황은 오직 생성자로 생성할 때 뿐입니다. 프로토콜을 타입으로 사용하는 습관을 길러두면 프로그램이 훨씬 유연해지는 장점이 있습니다. 나중에 구현 클래스를 교체하고자 한다면 그저 새 클래스의 생성자나 다른 정적 팩터리를 호출해주기만 하면 됩니다.

protocol Vehicle {
    func transport()
}

class Bicyle: Vehicle {
    func transport() {
        //...
    }
}


// 좋은 예. 프로토콜을 타입으로 사용함
let sonVehicle: Vehicle = Bicycle()

// 나쁜 예. 클래스를 타입으로 사용함
let sonVehicle: Bicycle = Bicycle()

그러면 앞선 선언은 아래처럼도 다른 코드는 전혀 손대지 않고 새로 구현한 클래스로 교체가 가능합니다. 한가지 주의 할 점은, 원래의 클래스가 프로토콜의 일반 규약 이외의 특별한 기능을 제공하며, 주변 코드가 이 기능에 기대어 동작한다면 새로운 클래스도 반드시 같은 기능을 제공해야 합니다.

class Car: Vehicle {
    func transport() {
        //...
    }  
}

let sonVehicle: Vehicle = Car()

하지만 이러한 제안이 모든 상황에 적용되지는 않고, 아래와 같은 상황은 프로토콜이 아닌 클래스로 참조해야 합니다.

  1. String, Int와 같은 타입처럼 적합한 인터페이스가 없는 경우
  2. 프레임워크가 제공하는 객체들 중 클래스 기반으로 작성된 경우
  3. 인터페이스에는 없는 별개의 메서드를 제공하는 클래스인 경우

실전에서는 주어진 객체를 표현할 적절한 프로토콜 혹은 상위 타입의 객체가 있는지 찾아서 해당 객체로 참조하면 더 유연하고 세련된 프로그램을 만들 수 있습니다. 적합한 객체가 없다면 클래스의 계층구조 중 필요한 기능을 만족하는 가장 덜 구체적인 (상위의)클래스를 타입으로 사용합니다.