객체지향 5원칙

cutiepazzipozzi·2023년 6월 27일
1

지식스택

목록 보기
35/35

이 포스팅을 어케 적게 되었냐 하면.. 앞선 불공변 얘기를 하며 리스코프 치환 원칙에 대한 언급이 나와서이다.. 5원칙을 얘기만 들어봤지 객체지향을 공부하며 더 찾아볼 생각을 하지 않아서,,

++ 어제 처음 알아본 개념이라, 조금 더 내용적인 설명이 필요할 것 같다,, 포스팅 마무리 아니에여,,,,ㅠㅠ

객체지향 5원칙

아래 다섯가지의 이름에서 앞 글자를 따 SOLID라고 불리기도 한다. 객체지향 원칙은 클린코드의 저자인 로버트 마틴에 의해 2009년에 명명되었다.

가볍게 객체지향 프로그래밍에 대해 정의해보자면 (내 주변의 아주 멋있는 개발자 say) 모의 실험을 통해 탄생한 '객체지향' 이라는 개념을 클래스를 통해 인스턴스를 만듦으로써 실현되는 프로그래밍 방식이다.

시작에 앞서,, 원칙

객체지향 그냥 내 기준에 맞춰서 프로그래밍 하면 되지 않나..? 싶을 수 있지만 실생활에서 생각해보자. 우리도 대한민국 법 아래에서 법(원칙)을 준수하며 살아가지 않은가? 그럼 왜 지키는가? 법을 지킴으로써 모두의 삶의 질을 높일 수 있지 않은가? (범죄 방지 등등..)

이제 소개할 객체지향의 5가지 원칙도 그러하다. 이 원칙을 지키면, 객체지향 프로그래밍을 더욱 효과적으로 수행할 수 있기 때문이다 (확장성 Good, 유지보수 Good 등등).

1. SRP (Single Responsibility Principle)

= 단일 책임 원칙

  • 모든(하나의) 클래스는 하나의 책임을 갖는다 (클래스의 단일책임).
    = 하나의 클래스는 하나의 기능에 대한 책임을 갖는다.
  • 단일책임 원칙 유무의 가장 큰 기준은 변경이다.
    (ex. 클래스 내에 변경이 발생하여도 파급효과가 작다면 단일책임 원칙도 작기 때문)
  • 가독성 향상, 유지보수 용이 (연쇄되어 있는 클래스 X)
  • 클래스명은 책임의 소재에 맞게 짓는다.

이해하기 쉽게 이 원칙에 대해 예시를 들자면, (인건비를 아끼기 위해) 학원에 수학과 영어를 모두 가르치는 선생님 한명만 뒀다고 생각해보자. 인건비를 아껴서 좋겠지만, 만약 선생님이 아프거나 일을 그만둔다면 당장 이 모든 수업을 감당할 선생님이 없어 학원은 수학 선생님, 영어 선생님을 모두 구해야 하므로 타격이 클 것이다.

class Car {
	private String brand;
    
    //자동차와 관련된 책임만을 가짐
    public void startEngine() {} //엔진 키는 로직
    public void accelerate() {} //가속하는 로직
}

2. OCP (Open Closed ~ )

= 개방 폐쇄 원칙

  • 확장에는 열려있고 수정에는 닫혀있다.
    = 기능을 확장하지만 그 기능에 관한 코드는 수정하지 않는다.
    (관리가 용이하고 재사용이 가능하기 때문)
  • 대표적인 예시가 추상화(인터페이스), 다형성(상속)
    -> 변할 여지가 다분한 부분은 추상화를 통해 클래스가 의존할 수 있도록 함
  • 하지만 변화의 여지를 개발자가 모두 예측할 수 없다는 것이 단점..
interface Shape {
	//메서드 내용이 변할 여지가 많으므로 추상화시킴
    double calculateArea();
}

class Circle implements Shape {
    private double radius;
    
    public double calculateArea() {
        return Math.PI * radius * radius;
    }
}

3. LSP (Liskov Substitution ~ )

= 리스코프 치환 원칙

  • 부모 클래스(기반 타입)는 자식 클래스(서브 타입)에 의해 대체될 수 있다.
  • 대표적인 예시가 상속, 제네릭 한정적 와일드 카드(는 내가 넣음ㅋ)
class Vehicle {
    // ...
    
    public void startEngine() {
        // 엔진을 시작하는 로직
    }
}

//Car 클래스는 Vehicle 타입으로 대체 가능
class Car extends Vehicle {
    // ...
    
    public void accelerate() {}
}

4. ISP (Interface Segregation ~ )

= 인터페이스 분리 원칙 (= 인터페이스의 단일 책임)

  • 자신이 사용하지 않는 인터페이스는 구현하지 않는다.
    -> 사용자는 자신이 사용하지 않는 메서드에 의존관계를 맺으면 안된다.

이도 이해하기 쉽게 예시를 들어본다. 동물이라는 인터페이스가 있다면, 강아지, 고양이 등의 클래스가 존재할 것이다. 우리는 강아지, 고양이 클래스를 사용하지 위해 동물 인터페이스를 구현하였다. 이때, 강아지 클래스를 수정하게 되면, 이것과 관련이 없는 고양이 클래스도 영향을 받게 된다.

//각각 프린트, 스캔 기능을 갖는 인터페이스 따로 구현
interface Printer {
    void print();
}

interface Scanner {
    void scan();
}

class FaxMachine implements Printer {
    public void print() {
    }
}

5. DIP (Dependency Injection ~ )

= 의존 역전 원칙

  • 의존관계를 맺을 때 변화가 없는 것에 의존하라.
    (= 추상적인 것이 구체적인 것에 의존해선 안된다)
  • 변화가 없는 것 -> ex. 인터페이스, 추상클래스
  • 고수준 모듈은 저수준 모듈의 구현에 의존해서는 안된다.

※ 의존성 주입 = 외부에서 두 객체 간의 관계를 결정하고 주입하는 것

interface DataAccessObject {
    void saveData(String data);
}

class DatabaseDAO implements DataAccessObject {
    public void saveData(String data) {
    }
}

class FileDAO implements DataAccessObject {
    public void saveData(String data) {
    }
}

class DataManager {
// 이 클래스는 DAO 인터페이스에 의존
// 실제 구현할 클래스는 런타임에 결정
    private DataAccessObject dao;

    public DataManager(DataAccessObject dao) {
        this.dao = dao;
    }

    public void processData(String data) {
        dao.saveData(data);
    }
}

++ 추가로 읽어보면 좋을 저자의 글

참조

https://inpa.tistory.com/entry/OOP-%F0%9F%92%A0-%EC%95%84%EC%A3%BC-%EC%89%BD%EA%B2%8C-%EC%9D%B4%ED%95%B4%ED%95%98%EB%8A%94-SRP-%EB%8B%A8%EC%9D%BC-%EC%B1%85%EC%9E%84-%EC%9B%90%EC%B9%99
https://sihyung92.oopy.io/oop/solid

profile
노션에서 자라는 중 (●'◡'●)

0개의 댓글