오늘은 소프트웨어 개발에서 중요한 원칙 중 하나인 SOLID 원칙에 대해 알아보겠습니다. SOLID는 객체 지향 프로그래밍에서 코드의 유지보수성과 확장성을 높이기 위해 고안된 다섯 가지 설계 원칙을 의미합니다. 이 글에서는 각 원칙의 개념과 중요성을 예제와 함께 설명하겠습니다.
SOLID는 다섯 가지 설계 원칙의 앞글자를 딴 약어입니다. 이 원칙들은 소프트웨어 디자인에서 흔히 발생하는 문제를 피하고, 코드의 품질을 향상시키는 데 도움을 줍니다. SOLID 원칙은 다음과 같습니다:
이제 각 원칙에 대해 자세히 알아보겠습니다.
SRP는 클래스는 하나의 책임만 가져야 한다는 원칙입니다. 즉, 클래스는 하나의 기능만을 가져야 하며, 그 기능을 변경해야 할 이유는 하나뿐이어야 합니다. 이렇게 하면 코드의 모듈성이 높아지고, 유지보수가 쉬워집니다.
class ReportGenerator {
public void generateReport() {
// 보고서 생성 로직
}
}
class EmailSender {
public void sendEmail() {
// 이메일 전송 로직
}
}
위 예제에서 ReportGenerator
클래스는 보고서 생성 책임만 가지고, EmailSender
클래스는 이메일 전송 책임만 가집니다. 이렇게 하면 각 클래스가 단일 책임을 가지게 되어 코드의 변경이 필요할 때 영향을 최소화할 수 있습니다.
OCP는 소프트웨어 개체는 확장에 열려 있어야 하고, 수정에는 닫혀 있어야 한다는 원칙입니다. 즉, 새로운 기능을 추가할 때 기존 코드를 수정하지 않고 확장할 수 있어야 합니다.
interface Shape {
double area();
}
class Circle implements Shape {
private double radius;
public Circle(double radius) {
this.radius = radius;
}
public double area() {
return Math.PI * radius * radius;
}
}
class Rectangle implements Shape {
private double width;
private double height;
public Rectangle(double width, double height) {
this.width = width;
this.height = height;
}
public double area() {
return width * height;
}
}
위 예제에서는 Shape
인터페이스를 구현하는 Circle
과 Rectangle
클래스가 있습니다. 새로운 도형을 추가하려면 Shape
인터페이스를 구현하는 새로운 클래스를 추가하면 되며, 기존 코드를 수정할 필요가 없습니다.
LSP는 자식 클래스는 언제나 자신의 부모 클래스를 대체할 수 있어야 한다는 원칙입니다. 즉, 자식 클래스는 부모 클래스의 기능을 모두 수행할 수 있어야 합니다.
class Bird {
public void fly() {
System.out.println("Flying");
}
}
class Sparrow extends Bird {
// Sparrow는 Bird의 fly 메소드를 제대로 구현합니다.
}
위 예제에서 Sparrow
클래스는 Bird
클래스의 기능을 그대로 상속받아 사용할 수 있습니다. 따라서 Sparrow
객체는 Bird
객체가 사용되는 곳에서 대체될 수 있습니다.
ISP는 특정 클라이언트를 위한 인터페이스 여러 개가 범용 인터페이스 하나보다 낫다는 원칙입니다. 즉, 클라이언트는 자신이 사용하지 않는 메소드에 의존하지 않아야 합니다.
interface Printer {
void print();
}
interface Scanner {
void scan();
}
class MultifunctionDevice implements Printer, Scanner {
public void print() {
// 인쇄 로직
}
public void scan() {
// 스캔 로직
}
}
위 예제에서는 Printer
와 Scanner
인터페이스를 분리하여 MultifunctionDevice
클래스가 각각의 기능을 구현하도록 합니다. 이렇게 하면 각 기능을 별도로 관리할 수 있어 유지보수가 용이해집니다.
DIP는 고수준 모듈은 저수준 모듈에 의존해서는 안 되며, 둘 다 추상화에 의존해야 한다는 원칙입니다. 즉, 구체적인 클래스보다는 인터페이스나 추상 클래스에 의존해야 합니다.
interface Keyboard {
void type();
}
class MechanicalKeyboard implements Keyboard {
public void type() {
// 기계식 키보드 타이핑 로직
}
}
class Computer {
private Keyboard keyboard;
public Computer(Keyboard keyboard) {
this.keyboard = keyboard;
}
public void type() {
keyboard.type();
}
}
위 예제에서 Computer
클래스는 Keyboard
인터페이스에 의존하며, MechanicalKeyboard
클래스는 Keyboard
인터페이스를 구현합니다. 이렇게 하면 Computer
클래스는 구체적인 MechanicalKeyboard
클래스에 의존하지 않게 되어 유연성이 높아집니다.
SOLID 원칙은 소프트웨어 설계의 기본이 되는 다섯 가지 원칙을 제시합니다. 이 원칙들을 잘 준수하면 코드의 유지보수성과 확장성이 크게 향상됩니다.