객체지향 프로그래밍(Object-Oriented Programming)

짱쫑·2022년 3월 8일
0
post-thumbnail

🦄 절차지향 프로그래밍(Procedural Programming)

  • 절차지향 프로그래밍은 이름에서 알 수 있듯이 물이 위에서 아래로 흐르틑 것처럼 순차적인 처리가 핵심으로 프로그램 전체가 유기적으로 연결되도록 만드는 프로그래밍 기법이다,

  • 객체를 순차적으로 처리하여 프로그램 전체가 유기적으로 연결되어야 하는 특징이 있고, 사용되는 대표 언어로는 C언어가 있다. 절차지향 프로그래밍의 장점은 컴퓨터의 작업 방식과 유사하기 때문에 실행속도가 빠르며, 시간적으로 유리하다는 점이다. 하지만 단점도 존재한다.

::객체지향 프로그래밍의 단점

  • 유지보수의 어려움
    • 모든 구성요소가 유기적으로 연결되어 있다는 말은, 하나가 고장났을 때 시스템 전체가 고장난다는 의미이며, 또한 문제를 해결하기 위해 일부분이 아닌 시스템 전체를 수리해야 한다는 말이기도 하다. 절차지향 언어는 이러한 유지보수가 매우 까다로운 편이고, 특히 프로그램의 문제를 디버깅하는 과정이 매우 어렵다.
  • 엄격하게 순서가 정해져있다
    • 실행 순서가 정해져 있기 때문에 코드의 순서가 바뀌면 결과가 달라질 가능성이 높다. 즉, 언어의 융통성이 부족하여 생산효율이 떨어진다. 예를들어 위의 사진에서 프로세스 3에서 문제가 발생했을 때 절차지향 프로그래밍은 다시 프로세스 1부터 디버깅을 하기 때문에 상당히 비효율적이다.

이러한 단점들은 스파게티 코드를 유발할 수 있고, 클린 아키텍처에서의 관심사 분리에 위배된다고 할 수 있다. 그래서 절차적 프로그래밍의 단점들을 보완하고, 효율적으로 분산 개발 및 협업과 유지보수를 향상시키기 위해 객체지향 프로그래밍이 등장하게 되었다.

🐴객체지향 프로그래밍(Object-Oriented Programming)

  • 객체 지향 프로그래밍은 컴퓨터 프로그래밍 패러다임 중 하나로, 프로그래밍에서 필요한 데이터를 추상화시켜 상태와 행위를 가진 객체를 만들고 그 객체들 간의 유기적인 상호작용을 통해 로직을 구성하는 프로그래밍 방법이다.

  • 객체지향 프로그래밍의 가장 큰 특징은 클래스를 이용해 연관 있는 처리 부분(함수)과 데이터분(변수)을 하나로 묶어 객체(인스턴스)를 생성해 사용한다는 점이다. 이 방식은 오늘날 가장 많이 사용하는 대표적인 프로그래밍 방식으로 대표적인 언어로는 JAVA, C++, C# 등이 있다.

이러한 특징을 가진 객체지향 프로그래밍은 클린 아키텍처의 관심사 분리를 위배했던 절차지향 프로그래밍과 달리, 관심사 분리를 지키면서 유지보수와 재사용성이 용이하다는 장점이 있다.

::클래스 & 객체(인스턴스)

  • 그림과 같이 클래스에 Car가 지정되어있고, 객체인 오브젝트에는 각 자동차의 유형인 아우디, 닛싼, 볼보가 나열되어 있다. 그림에서 유추할 수 있듯 클래스는 붕어빵 틀처럼 객체를 정의하고 만들어내기 위한 설계도 혹은 틀의 역할을 하며, 객체는 붕어빵 틀을 통해 만들어진 붕어빵과 같이 클래스를 통해 실제로 생성된 말 그대로 실체가 있는 객체를 의미한다.

클래스
객체를 정의하고 만들어 내기 위한 설계도 혹은 틀로써, 연관되어 있는 변수와 메서드의 집합을 의미한다. 클래스의 특징으로는 객체의 상태를 나타내는 필드와 객체의 행동을 나타내는 메소드로 구성되어 있으며, 필드에는 변수, 메소드에는 특정 작업을 수행하기 위한 명령문의 집합이 포함된다. 예시 코드에는 필드와 메소드가 생략되어 있는 Car라는 클래스가 선언되어 있다.

객체
클래스를 기반으로 생성된 실체로써, 실제 프로그램에서 사용되는 데이터를 의미한다. 즉 소프트웨어 세계에 실제로 구현될 대상을 의미한다는 말이다. 예시 코드에서는 선언을 통해 아우디, 닛싼, 볼보 등의 객체가 생성되며, 이후 메모리에 할당이 될 때 객체의 인스턴스화가 되는 것을 확인할 수 있다.

인스턴스
클래스를 기반으로 생성된 객체가 메모리에 할당되어 실제 사용될 때 인스턴스라고 부른다. 즉, 이러한 특징을 통해 인스턴스는 객체에 포함된다고 할 수 있다. 인스턴스는 추상적인 개념과 구체적인 객체 사이에 초점을 맞출 경우에 사용되고, 클래스를 기반으로 소프트웨어 세계에 구현된 실체라고 할 수 있다. 예시 코드에서와 같이 아우디, 닛싼, 볼보 등이 실제로 메모리에 할당 되면서 인스턴스화가 진행된다.

::클래스 VS 객체 VS 인스턴스


클래스 VS 객체
클래스와 객체의 관계에서 클래스가 설계도라 한다면, 객체는 설계도로 구현한 대상을 말할 수 있다.

객체 VS 인스턴스
객체와 인스턴스의 관계에서 객체는 타입으로 선언 되었을 때 객체라고 할 수 있으며, 인스턴스는 객체가 메모리에 할당되어 실제로 사용될 때 인스턴스라고 말할 수 있다.

🐗 객체지향 프로그래밍의 장단점


장점

  1. 협업에 용이함
  • 객체지향 프로그래밍에서는 프로젝트를 독립적인 객체 단위로 분리해 작업할 수 있기에 여러 개발자와 협업을 진행함에 있어서 용이한 장점이 있다.
  1. 대형 프로젝트에 적합
  • 클래스 단위로 모듈화 시켜 개발할 수 있기 때문에 개발을 유연하게 해주고, 변경 용이성을 높여준다. 이는 대형 프로젝트처럼 여러명, 여러 회사에서 프로젝트 개발을 할 때 업무 분담의 효율을 높여주기 때문에 대형 프로젝트에 적합하다는 장점을 가지고 있다.
  1. 유지보수 용이
  • 프로그램을 수정하거나 추가하는 경우에도, 캡슐화를 통해 주변에 미치는 영향이 적기 때문에 유지보수가 비교적 쉽다는 점이 있고, 매우 경제적이라는 점 또한 장점이다.
  1. 코드 재사용성 용이
  • 다른 사람이 만든 클래스를 재사용할 수 있으며, 상속을 통해 확장해서 사용할 수 있기 때문에 코드 재사용에 용이하다는 장점이있다.

단점

  1. 개발속도가 느림
  • 객체가 처리하려는 것에 대한 정확한 이해가 필요하기때문에 설계 단계부터 많은 시간이 모소된다.
  1. 실행속도가 느림
  • 객체 지향 언어는 절차 지향에 비해 대체적으로 실행속도가 느리다는 단점이 있다.
  1. 코딩의 난이도 상승
  • 다중 상속이 지원되는 C++같은 경우에 코드가 너무 복잡해질 수 있고, 코딩 난이도가 상승할 수 있다.
  1. 용량문제
  • 대규모 프로젝트에서처럼 객체가 많이 사용된다면 용량이 커질수 있다.

🐤 객체지향의 특징

::Monster 객체

  • 몬스터는 체력(healthPoint)과 레벨(level), 서식지(habitat) 속성을 가지고 있으며, 레벨을 1단계 올릴 수 있는 메소드(levelUp)을 가지고 있다. 이 특성들은 모든 몬스터들이 가지는 특성이므로 몬스터라는 클래스의 속성으로 부여해 줄 수 있다.

🐺캡슐화(Encapsulation)

  • 캡슐 알약을 보면 캡슐 안에 모든 필요한 약 성분들을 담아 내용물이 보이지 않게 하는데 이는 객체 내부에서 필요로 하는 정보를 은닉하는 캡슐화를 잘 보여주고 있다.

절차 지향에서는 요구변경 시 많은 코드의 수정이 필요하다. 그러나 객체지향에서는 클래스로 만들어 모듈화하기 때문에 요구변경에 대한 클래스 안에 메소드만 추가하여 사용자가 추가된 기능을 사용할 수 있게 만들어 이로 인해 코드의 수정이 최소화 된다는 장점이 있다.

  • 캡슐화는 객체의 데이터를 외부에서 직접 접근하지 못하게 막고, 함수를 통해서만 조작이 가능하게 하는 작업이다. 몬스터의 레벨(level)과 같은 경우는 이용자가 임의로 조작하게 하면 문제가 생길 가능성이 있다. 그래서 레벨업(levelUp)이라는 함수를 통해서만 레벨이 조작되어야 한다.

코드를 보면 외부에서 level변수에 직접 접근하지 못하도록 막는 캡슐화를 나타내고 있는데 이전의 몬스터 객체처럼 monster.level로 몬스터의 level데이터에 접근할 수 없게 되었다. level은 오직 monster.levelUp이라는 메소드(Method)로만 조작할 수 있게 되는 것이다.

::접근제한자

  • 이처럼 캡슐화는 접근제한자를 주어서 외부에서 데이터에 접근하는 것을 방지하고 받고자 하는 것들을 클래스로 만들어 반환하여 오로지 함수를 통해서만 접근할 수 있게 만드는 것을 말한다. 여기서 접근 제한자에는 접근 허용 정도에 따라 순서대로 public, protected, default, private가 있는데 public은 모든 접근을 허용하고, protectedsms default + 상속관계인 다른 패키지의 클래스, default는 같은 패키지만, private은 같은 클래스에서만 접근을 허용한다.

  • 캡슐화의 두 가지 관점에는 데이터의 캡슐화와 은닉화가 있는데 데이터 캡슐화객체의 상태와 행동을 하나의 단위로 묶는 자율적 실체를 말하고 은닉화외부에서 객체의 상태를 변경할 수 없도록 숨기는 것을 말한다.

::정보은닉

  • 캡슐화를 하는 중요한 목적은 바로 정보 은닉인데 이는 필요가 없는 정보는 외부에서 접근하지 못하도록 제한하는 것으로 private 키워드를 사용한다. 이렇게 보호된 변수는 getter나 setter 등의 메소드를 통해서만 간접적으로 접근이 가능하도록 하는 것이 캡슐화의 중요한 목적이다.

  • 그렇다면 정보은닉은 왜 필요할까? 은닉을 하지 않으면 객체의 상태정보에 누구나 접근이 가능해지고 이것은 잘못된 데이터가 들어갈 수 있다는 의미이다. 이를 방지하기 위해 정보은닉은 꼭 필요하게 되는 것이다. 그렇다면 이런 생각이 들 수 있다. '메소드를 사용하더라도 내가 잘못된 데이터를 넣을 수 있지 않을까?' 물론 그럴 수 있지만, 메소드를 사용했기 때문에 잘못된 데이터를 걸러낼 수 있으므로 메소드를 사용하지 않을 때와는 다르게 된다.

🐔 추상화(Abstraction)

  • 추상화의 사전적 의미는 특정한 개별사물과 관련되지 않은 공통된 속성이나 관계 등을 뽑아내는 것이다. 앞에 보이는 페르시안, 스핑크스, 먼치킨은 고양이 종류이다. 이들은 공통적으로 야옹하는 소리를 내고 꼬리가 있고, 다리가 네 개이다. 이를 고양이라는 클래스로 추상화할 수 있다. 이를 컴퓨터의 관점에서 생각해보면 추상화란 데이터나 프로세스 등을 의미가 비슷한 개념이나 표현으로 정의해나가는 과정이면서 동시에 각 개별 개체의 구현에 대한 상세함은 감추는 것이라고 할 수 있다.

  • 즉, 추상화는 객체들이 가진 공통의 특성들을 파악하고 불필요한 특성들을 제거하는 과정을 말한다. 객체들이 가진 동작들을 기준으로 이용자들이 동작만 쉽게 구동할 수 있도록 한다. 몬스터 예제에서 레벨업(levelUp)메소드를 실행만 하면 level이라는 속성을 컨트롤 할 수 있었던 것처럼 말이다. 이러한 추상화 과정을 통해 이용자들은 프로그래머가 만든 객체를 더 쉽게 사용할 수 있게 된다.

  • 추상화를 할 때 주의할 점은 속성 위주가 아닌 동작 위주로 정의하는 작업을 하는 것인데 객체의 동작에 연관이 되지 않은 속성들은 결국 불필요하다. 따라서 불필요한 속성들을 걸러내기 위해 동작을 먼저 정의하고, 동작에 필요한 속성들을 정리하는 것이 좋다.

  • 예를들어, 몬스터들이 공격을 받아 healthPoint가 0이 되면 필드에서 죽는 것(death)으로 처리하고 싶다. 이용자들은 몬스터들을 공격하고 죽는 모습을 보기만 하면 되고 어떠한 과정을 거쳐서 몬스터가 죽었는지에 대해서는 알 필요가 없다. 이 상황에서는 앞의 문스터 객체에 2가지 속성이 추가되어야 하는데 몬스터가 죽었는지 살았는지를 확인하는 속성과 healthPoint라는 속성이다.

  • 이제 몬스터는 체력(healthPoint)를 가지고 있고 데미지를 받아 체력이 깎이다가 체력이 0이 되면 죽는다. 이렇게 어떠한 동작과 속성을 정의하고 불필요한 정의들을 삭제하여 이용자가 편리하게 객체를 이용할 수 있도록 구성한 것이 추상화다.

::재사용성

  • OOP의 가장 큰 특성 중 하나가 바로 코드의 재사용성과 상속의 개념인데, 같은 객체를 여러 개 만들어야 하는 경우, 한 번 작성된 코드를 활용하여 동일한 객체를 만들 수 있다는 것이다. 예를들어 몬스터를 여러마리 만들어야 하는데 각 몬스터들은 모두 같은 속성을 가지고 있을 때 몬스터 객체를 이용하여 모든 몬스터들이 같은 속성과 메소드를 가질 수 있도록 만들 수 있다.

🐧 상속(Inheritance)

  • 상속은 객체지향의 꽃이라고 할 수 있는데 이는 기존 상위클래스에 근거하여 새롭게 클래스와 행위를 정의할 수 있게 도와주는 개념이다. 즉 기존 클래스에 기능을 가져와 재사용할 수 있으면서도 동시에 새롭게 만든 클래스에 새로운 기능을 추가할 수 있게 만들어준다. 예를들어 동물이라는 클래스의 속성을 강아지 클래스 또는 고양이 클래스가 물려받을 수 있다.

  • 상속이 필요한 이유는 코드의 중복을 막을 수 있어 코드가 더 간단해지므로 유지보수가 수월해지기 때문이다. OOP에서는 상속을 통해 코드의 중복 문제를 일부 해결할 수 있는데 동물 클래스에 여러 속성들을 정의해놓고 동물에 해당하는 종, 예를들면 강아지 클래스가 필요한 경우 동물 클래스와 상속 관계를 맺는다. 상속 관계를 맺으면 자식 객체를 생성할 때 부모 클래스의 속성들을 자동으로 물려받기 때문에 자식 클래스에서 또 정의할 필요가 없기 때문이다.

🐦 다형성(Polymorphism)

  • 다형성은 상속을 통해 기능을 확장하거나 변경하는 것을 가능케해준다. 즉 형태가 같은데 다른 긴으을 하는 것을 의미한다. 이를 통해 코드의 재사용, 코드가 짧아져 유지보수가 용이하도록 도와준다.

  • 예를들면 고양이 클래스에는 울음이라는 속성이 정의되어 있다고 하자, 사자는 고양이 과이기 때문에 사자 클래스가 고양이 클래스를 상속받는다고 하면, 사자 클래스에도 '울음'이라는 속성이 자동으로 추가된다. 그런데 고양이와 사자의 울음소리는 다르다. 같은 '울음' 속성임에도 실제 울음소리는 다르다. 바로 이런 것을 다형성이라고 말할 수 있다.

  • OOP에서 다형성의 개념을 녹여내는 방법은 두 가지가 있다. 바로 오버라이딩과 오버로딩이다.

::오버라이딩(Overriding)

  • 오버라이딩은 부모 클래스에서 상속받은 자식 클래스에서 부모 클래스에서 만들어진 메소드를 자식 클래스에서 자신의 입맛대로 다시 재정의해서 사용하는 것을 말한다.

  • 위의 코드에서 employee라는 클래스에서는 이름과 나이를 속성으로 가지고 있고 이를 상속받는 매니저 클래스에서는 employee 클래스에 있던 이름과 새로운 속성인 jobOfManagement를 이용해 재정의하여 사용한다. 따라서 위의 경우 사원의 이름 홍길동, 나이 30이고 아래는 관리자 홍길동은 영업담당입니다. 라는 결과가 나오게 된다.

::오버로딩

  • 오버로딩은 같은 이름의 메소드를 사용하지만 메소드마다 다른 용도로 사용되며 그 결과물도 다르게 구현할 수 있게 만드는 개념이다. 오버로딩이 가능하려면 메소드끼리 이름은 같지만 매개변수의 갯수나 데이터 타입이 달라야 한다.

  • 위 코드에서 이름이 test인 메소드가 총 세 개가 있지만 각각 매개변수의 유형과 갯수가 다른 것을 볼 수 있는데 차례대로 매개변수가 없는 test, 매개변수가 string형인 test, 그리고 매개변수가 string형과 int형을 가지고 있는 test이다. 호출 시 매개변수를 입력하면 호출 매개변수에 따라 매칭되어 함수를 실행시켜 준다. 따라서 결과는 사용자 없음, 사용자 이름은 홍길동, 사용자 이름은 홍길동 사용료는 5000이 되는 것이다.

  • 그럼 다형성을 사용하면 어떤점이 좋은가? 하면 같은 이름의 속성을 유지함으로서, 속성을 사용하기 위한 인터페이스를 유지하고, 메소드 이름을 낭비하지 않는다는 것이다. 예를들어 앞서 나왔던 고양이와 사자의 울음소리를 호출하기 위해서 각 객체에서 roar()이라는 메소드를 호출하면 된다. roarCat(), roarLion()으로 각각을 정의할 필요가 없다는 것이다. API가 많아질수록 복잡성을 증가하기 때문에 다형성은 유용하다고 할 수 있다.

profile
不怕慢, 只怕站

0개의 댓글