SOLID: Interface Segregation Principle (ISP)
“No client should be forced to depend on methods it does not use.”
Real World Analogy: Suppose, you go to a market to buy a charger for your iPhone.And you purchased a charger which has iPhone plug but also has Samsung, Nokia and Moto plugs too, which you never gonna use. That’s what an ISP solves, making sure you get an iPhone charger only.
Sometimes when we create interfaces we see the
- Empty implementations
- Big Interface with too many methods
- Methods are not relevant to each other (Low Cohesion)
This all indicates the violation of ISP.
To understand this lets see an example below,
protocol MediaPlayable {
func play()
func pause()
func download()
func seek(to time: TimeInterval)
var url: URL?
}
At first, glance it looks okay but when we have the implementation of this it would have the issues.
class AudioPlayer: MediaPlayable {
let player: ThirdPartyVideoPlayer?
func play() { player.playAudio() }
func pause() { player.pauseAudio() }
func download() { // Cant }
func seek(to time: TimeInterval) { player.seekAudio(to: time) }
}class VideoPlayer: MediaPlayable {
let player: VideoPlayer?
func play() { player.playVideo() }
func pause() { player.pauseVideo() }
func download() { // Cant }
func seek(to time: TimeInterval) { player.seekVideo(to: time) }
}class NetFlixPlayer: MediaPlayable {
let player: NFPlayer?
func play() { player.play() }
func pause() { player.pause() }
func download() { player.saveToGallery() }
func seek(to time: TimeInterval) { player.saveToGallery() }
}class TVPlayer: MediaPlayable {
let player: RemoteTVPlayer?
func play() { player.playTVStation() }
func pause() { player.stop() }
func download() { // Cant }
func seek(to time: TimeInterval) { // Cant }
}
Woaha ! so many empty methods, Meaning there is some issue at the abstraction layer.
Plus If download is called on AudioPlayer instance would not work same as the contract, breaking the LSV rule.
We can easily solve this by breaking or decomposing the MediaPlayable as below and make high cohesive small interfaces.
protocol Playable {
func play()
func pause()
}protocol Seekable {
func seek(to time: TimeInterval)
}protocol Downloadable {
func download()
}class AudioPlayer: Playable, Seekable {
let player: ThirdPartyVideoPlayer?
func play() { player.playAudio() }
func pause() { player.pauseAudio() }
func seek(to time: TimeInterval) { player.seekAudio(to: time) }
}class VideoPlayer: Playable, Seekable {
let player: VideoPlayer?
func play() { player.playVideo() }
func pause() { player.pauseVideo() }
func seek(to time: TimeInterval) { player.seekVideo(to: time) }
}class NetflixPlayer: Playable, Seekable, Downloadable {
let player: NFPlayer?
func play() { player.play() }
func pause() { player.pause() }
func download() { player.saveToGallery() }
func seek(to time: TimeInterval) { player.saveToGallery() }
}class TVPlayer: Playable {
let player: RemoteTVPlayer?
func play() { player.playTVStation() }
func pause() { player.stop() }
}
Fat protocols sound like “Too many responsibilities” for an interface (SRP)
& no empty implementations meaning more likely to break LSV violation.
Yes, all SOLID principles are intertwined with each other.
Lets move to our last rule Dependency Inversion Principle