객체지향 프로그래밍(Feat. SOLID)

밀크야살빼자·2024년 8월 24일
0

객체지향 프로그래밍

객체지향 프로그래밍이란?

프로그램에서 필요한 데이터를 추상화하여 상태와 행위를 가진 객체를 만들고, 그 객체들 간 상호작용을 통해 로직을 구성하는 프로그래밍 방식입니다.

장점

  • 코드 재사용이 용이 : 남이 만든 클래스를 가져와서 이용할 수 있고 상속을 통해 확장해서 사용할 수 있습니다.
  • 유지보수가 쉬움 : 절차 지향 프로그래밍에서는 코드를 수정해야할 때 일일이 찾아 수정해야하는 반면 객체 지향 프로그래밍에서는 수정해야 할 부분이 클래스 내부에 멤버 변수 혹은 메서드로 존재하기 때문에 해당 부분만 수정하면 됩니다.
  • 대형 프로젝트에 적합 : 클래스 단위로 모듈화 시켜서 개발할 수 있으므로 대형 프로젝트처럼 여러 명, 여러 회사에서 프로젝트를 개발할 때 업무 분담하기가 쉽습니다.

단점

  • 처리 속도가 상대적으로 느립니다.
  • 객체가 많으면 용량이 커질 수 있습니다.
  • 설계시 많은 시간과 노력이 필요합니다.

객체 지향 프로그래밍 5가지 키워드

  • 상속

    • 부모 클래스의 속성과 기능을 그대로 이어받아 사용할 수 있게하고 기능의 일부분을 변경해야 할 경우 상속받은 자식 클래스에서 해당 기능만 다시 수정(정의)하여 사용할 수 있게 하는 것입니다.
  • 캡슐화

    • 코드를 재수정 없이 재활용할 수 있으며, 접근 제어자를 통한 정보 은닉이 가능합니다.
    • 객체가 외부에 노출하지 않아야 할 정보 또는 기능을 접근제어자를 통해 적절히 제어 권한이 있는 객체에서만 접근하도록 할 수 있을 뿐만 아니라 관련된 기능과 특성을 한 곳에 모으고 분류하기 때문에 객체 재활용이 원활합니다.
  • 다형성

    • 하나의 변수명, 함수명 등이 상황에 따라 다른 의미로 해석될 수 있습니다.
    • 오버라이딩 : 부모 클래스의 메서드와 같은 이름, 매개변수를 재정의 하는 것입니다.
    • 오버로딩 : 같은 이름의 함수를 여러개 정의하고, 매개변수의 타입과 개수를 다르게 하여 매개변수에 따라 다르게 호출할 수 있게 하는 것입니다.
  • 추상화

    • 공통의 속성이나 기능을 묶어 이름을 붙이는 것
  • 클래스와 인스턴스(객체)

    • 클래스 : 집단에 속하는 속성과 행위를 변수와 메서드로 정의한 것입니다.
    • 객체 : 클래스에서 정의한 것을 토대로 실제 메모리에 할당된 것입니다.

인사이드 아웃으로 보는 객체지향 프로그래밍

캡슐화

코드를 나누고 정리해서 이해하기 쉽고 변경하기 쉽게 만드는 것이 핵심입니다.
마치 인사이드 아웃처럼 각 감정들의 역할이 있고 해당 역할이 필요하면 요청을 하고 설득하는 것처럼 설계해야합니다.
작고 독립적인 객체로 나눈 다음 그들이 서로 연결되어서 더 복잡한 시스템을 이루도록 만들어야 합니다.

예를 들어, 행복이라는 감정은 슬픔이라는 감정의 조작 방법을 알 수 없으며 슬픔이라는 감정 내부가 어떻게 이루어져 있는지 알지 못합니다.

이처럼 각 객체는 자신이 맡은 고유한 책임만 가지면 되고, 다른 객체가 접근 또는 내부를 알 수 없습니다.

메시징

객체에서 메시징은 객체들끼리 상호작용하는 것을 의미합니다.

메시징은 함수를 뜻합니다. 그럼 메시징(함수)는 무엇일까요?

  • 인터페이스를 설계하고 그것을 사용합니다.
  • 서로 다른 객체들간의 약속이며, 이 객체와 메시지가 만나서 프로그램이 됩니다.
  • 메시지를 통해서 반대 객체에게 요청을 합니다.

한가지 예시로, 오바카세와 서브웨이가 있습니다.
서브웨이에서는 손님이 직접 빵부터 토핑까지 하나하나 선택해야 합니다. 이 과정에서 손님은 계속해서 직원에게 세부 사항을 지시하고, 직원도 그에 맞춰 일일이 물어보며 준비해야 하죠. 이 방식은 손님에게 많은 선택을 맡기기 때문에 다소 번거로울 수 있습니다.

반면에 오마카세는 완전히 다릅니다. 손님이 어떤 요리를 주문할지, 소스나 재료를 어떻게 조합할지 고민할 필요가 전혀 없습니다. 셰프가 손님의 취향과 그날의 재료를 고려해 최적의 음식을 알아서 준비해줍니다. 즉, 손님은 세부적인 사항을 전혀 몰라도 단지 요청만 하면 전문가가 알아서 최선의 선택을 해주기 때문에 더욱 편안하고 효율적입니다.즉, 요청하는 쪽은 세부적인 디테일을 알 필요가 없고 요청만 하면 알아서 판단해서 줍니다.

따라서, 구체적인 지시를 내리기보다는 원하는 결과만 요청하는 것이 더 효율적입니다. 원하는 방향만 제시하고 필요한 준비물만 제공하면, 서로 간의 의사소통이 훨씬 단순해집니다. 중간에 발생하는 변경 사항이나 오해도 최소화할 수 있습니다. 이해가 쉽고 과정이 매끄러워지는 것이죠.

SOLID

단일 책임의 원칙(Single Responsibility Principle)

하나의 클래스는 하나의 책임만 갖는다는 원칙입니다.

장점

  • 가독성이 좋아진다.
  • 확장성이 좋아진다.
  • 재사용성이 좋아진다.

개방 폐쇄 원칙(Open/Closed Principle)

  • 클래스가 확장에는 개방되어 있고, 수정에는 닫혀있어야 한다는 원칙입니다.
  • 원본 코드를 수정하지 않고 새로운 동작을 추가할 수 있어야 합니다.
    • 예를 들어서, Calculator라는 클래스가 있고, 사각형과 원의 넓이를 구하는 함수가 있습니다. 그런데 오각형이나 육각형과 같은 더 많은 도형이 추가되어야 한다면, 그때마다 클래스가 계속 수정되어야 합니다.

      각 도형에 대한 계산이 아니라 shape라는 추상 클래스를 만들고 넓이를 구하는 메서드를 만든 후 Shape을 상속 해줍니다.

리스코프 치환 원칙 (LisKov’’s Substitution Principle)

  • 파생 클래스가 기본 클래스를 대체할 수 있어야 한다는 원칙입니다.
    • 대체할 수 있다는 말은, 자식 클래스는 최소한 자신의 부모 클래스에서 가능한 행위는 수행이 보장되어야 한다는 의미입니다.
    • 부모 클래스의 인스턴스를 사용하는 위치에 자식 클래스의 인스턴스를 대신 사용했을 때 코드가 원래 의도대로 작동해야 한다는 의미입니다.
  • 상속을 할때 지켜야하는 원칙입니다.
  • Vehicle이라는 클래스를 상속받아서 차, 트럭, 기차를 만들어 냈는데, 기차는 좌회전 우회전은 할 수 없습니다. 자동차를 상속 받아서 트럭, 스포츠카 등은 만들 수 있는데, 비행기는 만들 수 없다. 왜냐하면 자동차에 있는 기능들을 비행기 기능들로 대체할 수 없기 때문입니다.
  • 상속을 사용하는 것보다 여러 인터페이스를 조합해서 사용하는 것이 더 좋을 수 있습니다.

인터페이스 분리 원칙 (Interface Segregation Principle)

  • 인터페이스를 사용할 때 한 번에 크게 사용하지 말고, 작은 단위로 나눠서 사용하라는 원칙이다.
  • 인터페이스를 조합하는 형태로 발전 시켜 나가야 한다.

의존성 역전 원칙 (Dependency Inversion Principle)

  • 고수준 모듈이 저수준 모듈에서 직접 가져오면 안된다는 원칙이다.
    • 고소준 모듈 : 어떤 의미있는 단일 기능을 제공하는 모듈
    • 저수준 모듈 : 고수준 모듈의 기능을 구현하기 위해 필요한 하위 기능의 실제 구현
  • 의존성 역전을 지키기 위해서는 저수준 모듈이 고수준 모듈에서 정의한 추상 타입에 의존하도록 하며, 고수준 모듈은 저수준 모듈의 구현에 의존하면 안된다.
  • 자신보다 변하기 쉬운 것에 의존하지 않도록 한다.
  • 느슨한 결합이 가능하다.
  • 예를 들어서, Door라는 클래스가 있고 Door 클래스에는 Open과 Close 기능이 존재한다. 그리고 스위치라는 클래스에서 Door를 연결하고 문을 열고 닫고 할 수 있게 만들었다.
    치명적인 문제!!!! 스위치 클래스가 Door를 직접적으로 알고 있다는 점이다. 그렇기 때문에 스위치 클래스는 Door만 열고 닫을 수 있게 된다. 그런데 스위치라는 것은 문만 열고 닫는 용도가 아니다. 스위치 기능을 인터페이스를 만들고, 인터페이스에 활성/비활성하는 함수를 생성한다. 그리고 Door에 인터페이스를 작성해주면 된다. 스위치 클래스는 Door를 직접적으로 연결하는 것이 아니라 인터페이스를 통해서 연결하면 된다.

📚 참고자료

profile
기록기록기록기록기록

0개의 댓글