SOLID 재정립하기
객체 지향 개발론의 SOLID는 프로그래머가 시간이 지나도 유지 보수와 확장이 쉬운 소프트웨어를 만드는 것을 보장하고자 고안되었습니다
SRP(단일 책임 원칙)
단일 책임 원칙이란?
단일 책임 원칙은 "클래스는 단 한 개의 책임을 가져야 한다." 다른 말로 "클래스를 변경하는 이유는 단 한 개여야 한다." 를 의미하는 간단한 규칙이지만 간과하기 쉬움으로 주의가 필요한 원칙입니다
단일 책임 원칙은 깨끗하고 체계적인 소프트웨어에 시작임으로, 자신이 설계한 클래스가 정말 하나의 책임만 가지고 있는지? 항상 검토해야 합니다
단일 책임 원칙을 지켰는지 검사하는 간단한 방법
클린코드 저서에 따르면, 단일 책임 원칙을 지켰는지 검사하는 간단한 방법은 클래스 명이 만일, 그리고, 하며, 하지만 등을 사용하지 않고 25자 내로 설명이 가능해야 하며, 다른 말로 대체될 수 없어야 한다고 합니다
또한, 클래스의 특정 맴버 변수가 특정 메서드에서만 활용된다면 독자적인 클래스로 구분될 확률이 높습니다
- 클래스의 메서드들이 맴버 변수를 많이 활용하는 것이 응집도를 나타내는 지표이기 때문입니다
클래스가 많아지면 의존관계가 늘어나지 않는가?
하나의 클래스를 작게 분리하면 어디에 어떤게 있는지 문맥을 파악하기 용이하고 수정이 쉽다는 장점에 반해 의존관계가 늘어나기 때문에 DIP와 OCP를 준수하도록해 의존성을 낮춰야합니다
OCP(개방 폐쇄 원칙)
개방 폐쇄 원칙은 "기능을 변경하거나 확장할 수 있으면서 그 기능을 사용하는 기존 코드는 수정하지 않는다." 입니다
다형성을 통해 구현이 가능하며, 변경이 필요할때 기존 클래스를 수정하지 않고, 인터페이스를 구현한 새로운 클래스를 하나 만들어 새로운 기능을 구현하면 됩니다
다형성을 활용 했더라도 아래와 같이 직접 의존성을 주입하게 되면, 새로운 구현체로 변경할때 마다 기존 코드를 수정해야 합니다
public class A{
private final B b = new Bimple();
}
이를 해결하는 방법은 DIP에서 설명하도록 하겠습니다
DIP(의존 관계 역전)
의존 관계 역전은 "추상화에 의존해야지, 구체화에 의존하면 안된다.” 라는 의미입니다
아래 코드는 추상화 B와 구현체 Bimple 모두에게 의존하고 있음으로 DIP를 불만족 합니다
public class A {
private final B b = new Bimple();
}
DIP와 OCP 만족하기
의존 관계 주입을 담당하는 AppConfig를 통해 해결이 가능합니다
이렇게 실제 코드에는 구현체에 대한 의존을 지우고, 외부인 AppConfig에서 의존관계 주입을 모두 관리하게 됩니다
public class GameComputer{
private ComputerNumbersGenerator computerNumbersGenerator;
private ComputerHintGenerator computerHintGenerator;
private ComputerGameEndChecker computerGameEndChecker;
...
}
//의존 관계 주입은 여기서만 한다
public class AppConfig {
public static GameComputer createGameComputer() {
return new GameComputerImple(createComputerNumbersGenerator()
, createComputerHintGenerator()
, createComputerGameEndChecker());
}
public static ComputerNumbersGenerator createComputerNumbersGenerator() {
return new ComputerNumbersGeneratorImple();
}
public static ComputerHintGenerator createComputerHintGenerator() {
return new ComputerHintGeneratorImple();
}
public static ComputerGameEndChecker createComputerGameEndChecker() {
return new ComputerGameEndCheckerImple();
}
}
LSP(리스코프 치환 원칙)
리스코프 치환 원칙은 "상위 타입의 객체를 하위 타입의 객체로 치환해도 상위 타입을 사용하는 프로그램은 정상적으로 동작해야 한다." 를 의미합니다
따라서, 상위 타입에서 정한 명세를 하위 타입에서도 그대로 지킬 수 있을 때 상속을 해야 합니다.
자식은 부모에서 정한 명세를 변경하거나 확장하면 안된다는 것입니다, 상속을 하게 된다면 해당 메소드를 재정의하지 않아야 합니다.
상속을 잘 정의할 필요도 있지만, 혹시 모를 위험을 방지하기 위해 불변 객체를 활용할 수 있습니다
ISP(인터페이스 분리 원칙)
인터페이스 분리 원칙은 "클라이언트는 자신이 사용하는 메소드에만 의존해야 한다." 를 의미합니다.
인터페이스 A를 구현하는 X와 Y가 있다. 앞으로 A를 구현하여 생성할 다른 클래스들이 g라는 메서드를 필요로 한다. X는 g가 필요하고 Y는 g가 필요하지 않다. 어떻게 설계하는게 바람직한가?
이때, Y는 g를 필요로 하지 않음으로 A에 g를 정의해서는 안됩니다. 별도의 g를 가진 인터페이스 B를 생성하여 이를 사용하는 클래스에 구현해야 합니다
Reference
https://mangkyu.tistory.com/226
https://abcdefgh123123.tistory.com/347
https://devkingdom.tistory.com/296
https://yoongrammer.tistory.com/96
https://velog.io/@gooreum_90/SRP단일-책임-원칙
https://steady-coding.tistory.com/370