✅ 나름대로의 원칙 : 추상성이 낮은 클래스보다 추상성이 높은 클래스와 통신을 하는 것을 의미한다. 추상성이 낮은 경우 세부 구현에 의존성이 높아질 수 있고, 추상성이 높은 경우 보다 유연하고 재사용 가능한 코드를 작성할 수 있다.
// 추상성이 낮은 클래스
class Dog {
fun bark() {
println("멍멍")
}
}
// 추상성이 높은 클래스 또는 인터페이스
interface Animal {
fun makeSound()
}
// 추상성이 높은 클래스 또는 인터페이스를 사용하는 클라이언트 코드
class AnimalSoundPlayer {
fun playSound(animal: Animal) {
animal.makeSound()
}
}
// Dog 클래스를 Animal 인터페이스에 맞춰 확장
class DogAdapter : Animal {
private val dog = Dog()
override fun makeSound() {
dog.bark()
}
}
✅ 클래스 간 의존 관계 : 한 클래스가 어떤 기능을 수행하려고 할 때, 다른 클래스의 서비스가 필요한 경우를 말한다. 대표적으로 A 클래스의 메소드에서 매개변수를 다른 B 클래스의 타입으로 받아 B 객체의 메서드를 사용할 때, A 클래스는 B 클래스와 의존한다고 보면 된다.
// 인터페이스
interface Toy
class Robot : Toy
class Lego : Toy
class Doll : Toy
// 클라이언트
class Kid {
var toy: Toy? = null // 합성
fun setToY(toy: Toy) {
this.toy = toy
}
fun play() {
println("Kid is playing with ${toy?.javaClass?.simpleName}")
}
}
// 메인 메소드
fun main() {
val boy = Kid()
// 1. 아이가 로봇을 가지고 놀 때
val robotToy: Toy = Robot()
boy.setToY(robotToy)
boy.play() // Kid is playing with Robot
// ...
// 2. 아이가 레고를 가지고 놀 때
val legoToy: Toy = Lego()
boy.setToY(legoToy)
boy.play() // Kid is playing with Lego
}
실제 자바에서 인터페이스에 대해 학습할 때 매개변수로 객체를 받을 때 구체 클래스 타입으로 받는게 아니라, 다형성을 이용해 인터페이스 타입으로 통신하는 것이 좋다고 배웠을 것이다.
구체 클래스 : 객체를 생성할 수 있는 클래스
// 변수 타입을 고수준의 모듈인 인터페이스 타입으로 선언하여 저수준의 모듈을 할당
List<String> myList = new ArrayList()<>;
Set<String> mySet = new HashSet()<>;
Map<int, String> myMap = new HashMap()<>;
의존 역전 원칙 : 자신보다 변하기 쉬운 것에 의존하던 것을 추상화된 인터페이스나 상위 클래스를 두어 변하기 쉬운 것의 변화에 영향받지 않게 하는 원칙
class OneHandSword(val name: String, val damage: Int) {
fun attack(): Int {
return damage
}
}
class TwoHandSword { /*...*/ }
class BattleAxe { /*...*/ }
class WarHammer { /*...*/ }
class Character(
val name: String,
var health: Int,
var weapon: OneHandSword // 의존 저수준 객체
) {
fun attack(): Int {
return weapon.attack()
}
fun changeWeapon(newWeapon: OneHandSword) {
weapon = newWeapon
}
fun getInfo() {
println("이름: $name")
println("체력: $health")
println("무기: $weapon")
}
}
// 고수준 모듈
interface Weaponable {
fun attack(): Int
}
class OneHandSword(val name: String, val damage: Int) : Weaponable {
override fun attack(): Int {
return damage
}
}
class TwoHandSword : Weaponable { /*...*/ }
class BattleAxe : Weaponable { /*...*/ }
class WarHammer : Weaponable { /*...*/ }
class Character(val name: String, var health: Int, var weapon: Weaponable) {
fun attack(): Int {
return weapon.attack()
}
fun changeWeapon(newWeapon: Weaponable) {
weapon = newWeapon
}
fun getInfo() {
println("이름: $name")
println("체력: $health")
println("무기: $weapon")
}
}