[SWAD] Ep.12 SOLID

GLICO·2024년 10월 25일

SWAD

목록 보기
12/12

Contents

  • SOLID 설계 원칙

Introduction to SOLID Principles

Introduction to SOLID

  • 객체지향 소프트웨어를 견고하고 확장 가능하게 설계 하기 위한 다섯가지 기본 원칙

  • SRP : 단일 책임 원칙

  • OCP : 개방-폐쇄 원칙

  • LSP : 리스코프 치환 원칙

  • ISP : 인터페이스 분리 원칙

  • DIP : 의존성 역전 원칙

Single Responsiblility Principle (SRP)

SRP

모든 클래스는 단 하나의 책임만을 가져야한다.

  • 책임 : 객체가 "해야하는 것" 또는 "할 수 있는 것" (기능과 관련)

  • 즉, 하나의 클래스는 하나의 기능만을 담당하여 하나의 책임을 수행하는데 집중

SRP가 지켜지지 않으면 "변경"에 취약

  • 예측하지 못한 변경사항이 발생하더라도 유연하고 확장성 있게 시스템 구조를 설계 해야함 (유지보수 성을 높이기 위함)

책임 분리

  • 한 클래스에서 너무 많은 책임을 부여하지 말고 단 하나의 책임만 수행하도록 변경하는 것

Open-Closed Principle (OCP)

OCP

확장에는 열려있어야 하며, 수정에는 닫혀있어야 한다.

  • 확장에 열림 => 변경 사항이 발생했을 때 기존의 코드를 유연하게 확장할 수 있어야 함

  • 수정에 닫힘 => 이때, 기존의 코드에는 수정이 없어야 함

  • 추상화를 통해 상속 및 다형성 구조를 잘 마련해 두면 OCP를 손쉽게 따라갈 수 있음

  • 일반적으로 추상 클래스와 상속을 통한 클래스 관계 구축을 통해 OCP를 따름

Liskov Substitution Principle (LSP)

LSP

상위 타입의 객체를 하위 타입의 객체로 치환해도 상위 타입을 사용하는 프로그램은 정상적으로 동작해야 한다.

  • 이를 위해서는 자식 클래스가 부모 클래스의 행동 규약을 지켜야함
  • 다형성에 관련된 원칙!
void myData() {
	Collection data = new LinkedList();
    data = new HashSet();
    
    modify(data);
    
void modify(Collection data) {
	data.add(1);
    ...
}

data에 하위 클래스의 객체가 주어져도 modify 함수는 정상적으로 동작해야 한다.

LSP가 지켜지지 않은 예시 - Overriding 오류

class Animal {
	int speed = 100;
    
    int go(int distance) {
		return speed * distance;
    }
}

class Eagle extends Anlmal {
	String go(int distance, boolean flying) {
    	if (flying)
        	return distance + "만큼 날아감";
        else
        	return distance + "만큼 걸어감";
}

부모의 go 함수를 제대로 오버라이딩 하지 않으면 다형성이 작동되지 않음

LSP가 지켜지지 않은 예시 - 부모의 의도와 다른 Overriding

LSP가 지켜지지 않은 예시 - 잘못된 상속 관계 구성

  • A 개발자가 상속 구조의 호환성을 위해 Fish에서 speak은 구현하되 의미없는 동작을 하게 함 (예외를 출력)
  • B 개발자는 상속 구조를 보고 LSP에 입각해 위와 같이 list에서 사용을 했는데 예상치 못한 예외가 출력됨 (협력 시 상호 신뢰를 잃게 될 수 있음)

  • 코드를 수정한다면 speak은 따로 빼서 필요한 클래스에서만 구현하게 상속 구조를 수정
    B 개발자는 위 상속 구조를 보고 speak을 할 수 있는 객체만 리스트에 담을 수 있음 (LSP를 따른다면 Fish를 리스트에 포함시키지 않는 방향이 되어야 함)

Interface Segregation Principle (ISP)

ISP

클라이언트가 사용하지 않는 메소드에 의존하지 않아야 한다.

  • 인터페이스에 클라이언트가 필요한 메소드만 포함될 수 있도록 분리해야 한다
  • 인터페이스를 각각 사용에 맞게 분리하여 인터페이스의 단일 책임을 강조

SRP vs ISP

  • SRP는 클래스가 단일 책임을 갖도록 하는 원칙 (클래스 분리)
  • ISP는 인터페이스의 단일 책을 강조 (인터페이스 분리)

Dependency Inversion Principle (DIP)

DIP

의존 관계는 변화하기 어렵거나 변화가 거의 없는 것과 맺어야 한다.

  • 객체들이 서로 정보를 주고 받을 때 의존 관계가 형성됨

  • 이때, 객체들은 추상성이 낮은 클래스보다 추상성이 높은 클래스와 의존 관계를 맺는 것이 각 클래스간의 결합도를 낮출 수 있음

  • 변하기 어려운 추상적인 것들을 표현하는 수단으로 추상클래스와 인터페이스가 있음

  • DIP를 만족하려면 추상클래스 또는 인터페이스와 의존 관계를 맺도록 설계해야 함

Example

  • 기존에는 고수준의 클래스가 저수준의 클래스에 직접적으로 의존하는 방식으로 구현되는 경우가 많았음
  • DIP에서는 이를 깨고, 고수준 클래스와 저수준 클래스 둘 다 추상화에 의존해야함을 의미
  • 기존의 의존 구도를 뒤집는 것을 역전(Inversion)이라고 표현
profile
Its me Glico

0개의 댓글