강한 결합과 느슨한 결합
강한 결합
객체간 의존 관계에서 강한 결합이란, 어떠한 객체가 다른 객체에 강한 의존성을 가지고 있다는 것이다.
이는 구현 클래스(Concrete Class)를 직접 참조하고 있음을 의미한다.
public class Person {
private Chicken chicken;
public Person() {
this.chicken = new Chicken();
}
public void startEat() {
chicken.eat();
}
}
public class Chicken {
public void eat() {
System.out.println("치킨을 먹습니다.");
}
}
위와 같은 코드가 존재할 때, Person 클래스는 Chicken 클래스를 직접 참조하고 있다. 즉, Person 클래스가 Chicken 클래스에 의존하고 있다.
이 코드는 치명적인 단점이 있다.
- Chicken 클래스가 없으면 Person 클래스를 정의할 수 없다.
- Chicken 클래스를 다른 종류의 음식 클래스로 바꾸게 되면 Person 클래스의 코드 대부분이 변경된다.
단점 2에 대한 예시로, Person 클래스 안의 Chicken 클래스를 Pizza 클래스로 변경한다고 하면 코드의 변경은 다음과 같이 이루어질 것이다.
위 변화를 보면, 멤버 변수 타입(클래스) 하나만 바꿨을 뿐인데 Person 클래스에 있는 대다수의 코드가 변경된다.
그렇기 때문에 강한 결합(강한 의존성) 이라고 하는 것이며, 이는 유지보수 측면에서 굉장히 좋지 않은 케이스라고 할 수 있다.
느슨한 결합
그래서 등장하게 된 것이 인터페이스를 통한 느슨한 결합이다.
느슨한 결합은 구현 클래스를 직접 참조하는 강한 결합과 달리 '추상화'에 의존한다. 결합도의 관점에서 구체 클래스 의존에서 인터페이스 의존으로 갈수록 Client가 알아야 할 지식의 양이 적어지므로 결합도가 느슨해진다.
public interface Food {
void eat();
}
public class Chicken implements Food {
@Override
public void eat() {
System.out.println("치킨을 먹습니다.");
}
}
public class Pizza implements Food {
@Override
public void eat() {
System.out.println("피자를 먹습니다.");
}
}
public class Person {
private Food food;
public Person(Food food) {
this.food = food;
}
public void startEat() {
food.eat();
}
}
위와 같이 Food 인터페이스를 만들어 eat() 추상 메소드를 선언하여 Chicken 클래스와 Pizza 클래스에서 eat()를 구현하게 하면, Chicken 오브젝트와 Pizza 오브젝트는 모두 Food 타입에 대입될 수 있으므로 Person 클래스에서 멤버변수 타입을 Food로 할 수 있게 된다.
이는 Person 클래스 내부적으로 코드의 변경이 일어날 필요 없이, 생성자를 통해 객체를 받아 멤버변수에 대입하기만 하면 오브젝트를 변경 가능하게 해준다.
즉, 강한 결합에서 벗어나 약한 결합을 이루게 해주는 것이다.
출처