Clean Architecture

[Clean Architecture] SOLID원칙 05. ISP(Interface Segregation Principle) 인터페이스 분리 원칙

clamp 2023. 5. 5. 19:49

클라이언트는 그들이 사용하지 않는 인터페이스에 의존해서는 안된다.

 

불필요한 인터페이스 요소들을 포함시키지 말라는 의미이다. 불필요한 요소들이 포함되면서 복잡해지고, 무거워짐에 따라 진작 원하는 정보를 얻을 수 없는 지경에까지 이르기도 한다. 이 문제는 클래스나 프로토콜 모두에게 영향을 줄 수 있다.

 

Protocol

 

protocol의 경우를 먼저 본다.

"터치"를 했을 때 반응을 구현해줄 didTap 메서드를 가지고 있는 GuestureProtocol을 보자.

 

protocol GusetureProtocol{
    func didTap()
}

이후에 더 많은 제스처들을 추가해주고싶어서 프로토콜에 추가한다면?

protocol GusetureProtocol{
    func didTap()
    func didDoubleTap()
    func didLongPress()
}

그리고 실제로 사용할 때에는 프로토콜을 채택하고 그 메서드들을 구현해주면 된다.

class SuperButton: GusetureProtocol{
    func didTap() {
        <#code#>
    }
    
    func didDoubleTap() {
        <#code#>
    }
    
    func didLongPress() {
        <#code#>
    }
    
    
}

문제는 여기서 발생한다 만약 didTap만을 지원하는 버튼을 만들고자 한다면 어떻게 해야할까?  따로 메서드를 만들지, 아니면 프로토콜을 채택하고 나머지 메서드들은 더미하게 구현해야할지 싶을거다.

 

하지만 이렇게 구현하는 것 자체가 ISP를 위반하는 "Fat"한 인터페이스이다. 그렇기에 필요한 기능만 골라서 사용할 수 있도록 프로토콜을 나눠서 구현해주는 방식으로 구성해야한다.

protocol TapProtocol {
    func didTap()
}

protocol DoubleTapProtocol {
    func didDoubleTap()
}

protocol LongPressProtocol {
    func didLongPress()
}

class SuperButton: TapProtocol, DoubleTapProtocol, LongPressProtocol {
    func didTap() {
        // send tap action
    }

    func didDoubleTap() {
        // send double tap action
    }

    func didLongPress() {
        // send long press action
    }
}

class PoorButton: TapProtocol {
    func didTap() {
        // send tap action
    }
}

이렇게 프로토콜마다 메서드를 하나씩 만들어주게되면, 원하는 프로토콜만 채택하여 기능을 구현할 수 있게 된다.

 

 

Class의 경우

영상에 대한 정보를 포함하고 있는 Video라는 클래스가 있다고 해보자.

class Video {
    var title: String = "My Video"
    var description: String = "This is a beautiful video"
    var author: String = "Marco Santarossa"
    var url: String = "https://marcosantadev.com/my_video"
    var duration: Int = 60
    var created: Date = Date()
    var update: Date = Date()
}

그리고 play라는 메서드에 비디오 정보를 넣어준다.

func play(video: Video) {
    // load the player UI
    // load the content at video.url
    // add video.title to the player UI title
    // update the player scrubber with video.duration
}

 

play메서드에는 title, rul, duration만 필수적인 정보이고 나머지는 굳이 받아야 할 필요가 없다.

 

이런 경우 또한 protocol을 정의해줌으로써 play메서드가 필요로하는 정보만 전달해주면 된다.

protocol Playable {
    var title: String { get }
    var url: String { get }
    var duration: Int { get }
}

class Video: Playable {
    var title: String = "My Video"
    var description: String = "This is a beautiful video"
    var author: String = "Marco Santarossa"
    var url: String = "https://marcosantadev.com/my_video"
    var duration: Int = 60
    var created: Date = Date()
    var update: Date = Date()
}


func play(video: Playable) {
    // load the player UI
    // load the content at video.url
    // add video.title to the player UI title
    // update the player scrubber with video.duration
}