SOLID 원칙이란 객체지향 설계에서 유지보수 가능하고, 확장 가능한 코드를 위한 5가지 설계 원칙의 앞글자를 따서 만든 원칙입니다.
객체 지향 프로그래밍에서 SOLID 원칙을 적용하면 유지보수가 쉽고 코드 확장에 유연해지고 테스트하기 좋고 협업 시 충돌 최소화가 가능해져 개발의 생산성을 높일 수 있습니다.
❗ SOLID 원칙은 특정 문제를 해결하기 위한 지침일 뿐이기 때문에 꼭 프로젝트에 전부 적용할 필요가 없으며 상황에 따라 선택하면 된다.
class Product {
private String name;
private int price;
public void save() {/* 상품 정보 저장 */}
public void print() {/* 상품 정보 출력 */}
}
class Product {
private String name;
private int price;
}
class ProductSaver {
public void save(Product product) {...}
}
class ProductPrinter {
public void print(Product product) {...}
}
class ShapePainter {
public void draw(String shape) {
if (shape.equals("triangle")) {
System.out.println("삼각형");
} else if (shape.equals("square")) {
System.out.println("사각형");
}
}
}
draw() 메소드를 변경해야 한다.interface Shape {
void draw();
}
class Triangle implements Shape {
public void draw() {
System.out.println("삼각형");
}
}
class Square implements Shape {
public void draw() {
System.out.println("사각형");
}
}
class ShapePainter {
private Shape shape;
public ShapePainter(Shape shape) {
this.shape = shape;
}
public void drawShape() {
shape.draw();
}
}
ShapePainter는 전혀 수정하지 않아도 됨class Bird {
public void fly() {...}
}
class Penguin extends Bird {
public void fly() { throw new UnsupportedOperationException();}
}
Penguin은 Bird지만 날지 못함 -> Bird로서 날아야한다는 계약 위반✍️ 사전 조건(Pre-condition)
class BankAccount {
public void withdraw(int amount) {...}
}
class SavingsAccount {
public void withdraw(int amount) {
if (amount < 10000) { throw new IllegalArgumentException(); }
}
}
BankAccount는 모든 금액에 대해 인출이 가능한데 SavingsAccount에 제약 조건이 추가됨✍️ 사후 조건(Post-condition)
class Shape {
private int area;
public int getArea() { return area; }
}
class FailingShape {
public int getArea() { return null; }
}
❗즉, LSP는 안정적인 다형성을 위해 필요하다.
// 복합기
interface MultiFunctionDevice {
void print();
void scan();
void fax();
}
class SimplePrinter implements MultiFunctionDevice {
@Override
public void print() {...}
@Override
public void scan() { throw new UnsupportedOperationException(); }
@Override
public void fax() { throw new UnsupportedOperationException(); }
}
SimplePrinter는 print 기능만 사용하는데 scan과 fax 기능의 구현을 강제 당함interface Printer {
void print();
}
interface Scanner {
void scan();
}
interface Fax() {
void fax();
}
class SimplePrinter implements Printer {
public void print() { ... }
}
class SimpleScanner implements Scanner {
public void scan() { ... }
}
class HomeMultiFunctionalPrinter implements Printer, Scanner {
public void print() { ... }
public void scan() { ... }
}
Q. '응집도가 높다'는 것은?
A. 인터페이스에 포함된 메소드들이 하나의 목적을 공유하고 있다는 뜻
class LightBulb {
public void turnOn() {
System.out.println("LightBulb is turned on.");
}
public void turnOff() {
System.out.println("LightBulb is turned off.");
}
}
class Switch {
private LightBulb lightBulb;
public Switch() {
// DIP 위반: 구체적인 구현(LightBulb)에 직접 의존
lightBulb = new LightBulb();
}
public void operate(String command) {
if (command.equals("on")) {
lightBulb.turnOn();
} else if (command.equals("off")) {
lightBulb.turnOff();
}
}
}
Switch는 LightBulb에 강하게 결합됨LightBulb를 NeonBulb등으로 바꾸기 위해서는 직접 수정해야한다. interface Switchable {
void turnOn();
void turnOff();
}
class LightBulb implements Switchable {
@Override
public void turnOn() {
System.out.println("LightBulb is turned on.");
}
@Override
public void turnOff() {
System.out.println("LightBulb is turned off.");
}
}
class NeonBulb implements Switchable {
@Override
public void turnOn() {
System.out.println("NeonBulb is turned on.");
}
@Override
public void turnOff() {
System.out.println("NeonBulb is turned off.");
}
}
class Switch {
private Switchable device;
// DIP 준수: 생성자 주입을 통해 추상화(Switchable)에 의존
public Switch(Switchable device) {
this.device = device;
}
public void operate(String command) {
if (command.equals("on")) {
device.turnOn();
} else if (command.equals("off")) {
device.turnOff();
}
}
}
Switch는 Switchable라는 추상화에만 의존Switchable이 와도 유연하게 대체 가능