프로그램 전체가 유기적으로 연결되도록 만드는 프로그래밍 기법
이다,컴퓨터의 작업 방식과 유사하기 때문에 실행속도가 빠르며
, 시간적으로 유리하다는 점이다. 하지만 단점도 존재한다.::객체지향 프로그래밍의 단점
하나가 고장났을 때 시스템 전체가 고장
난다는 의미이며, 또한 문제를 해결하기 위해 일부분이 아닌 시스템 전체를 수리해야 한다는 말이기도 하다. 절차지향 언어는 이러한 유지보수가 매우 까다로운 편이고, 특히 프로그램의 문제를 디버깅하는 과정이 매우 어렵다.이러한 단점들은 스파게티 코드를 유발
할 수 있고, 클린 아키텍처에서의 관심사 분리에 위배
된다고 할 수 있다. 그래서 절차적 프로그래밍의 단점들을 보완하고, 효율적으로 분산 개발 및 협업과 유지보수를 향상시키기 위해 객체지향 프로그래밍이 등장하게 되었다.
프로그래밍에서 필요한 데이터를 추상화시켜 상태와 행위를 가진 객체를 만들고 그 객체들 간의 유기적인 상호작용을 통해 로직을 구성하는
프로그래밍 방법이다.이러한 특징을 가진 객체지향 프로그래밍은 클린 아키텍처의 관심사 분리를 위배했던 절차지향 프로그래밍과 달리, 관심사 분리를 지키면서 유지보수와 재사용성이 용이하다는 장점
이 있다.
붕어빵 틀처럼 객체를 정의하고 만들어내기 위한 설계도 혹은 틀의 역할
을 하며, 객체는 붕어빵 틀을 통해 만들어진 붕어빵과 같이 클래스를 통해 실제로 생성된
말 그대로 실체가 있는 객체를 의미한다.클래스
객체를 정의하고 만들어 내기 위한 설계도 혹은 틀로써, 연관되어 있는 변수와 메서드의 집합을 의미
한다. 클래스의 특징으로는 객체의 상태를 나타내는 필드와 객체의 행동을 나타내는 메소드로 구성되어 있으며, 필드에는 변수
, 메소드에는 특정 작업을 수행하기 위한 명령문의 집합이 포함
된다. 예시 코드에는 필드와 메소드가 생략되어 있는 Car라는 클래스가 선언되어 있다.
객체
클래스를 기반으로 생성된 실체로써, 실제 프로그램에서 사용되는 데이터를 의미
한다. 즉 소프트웨어 세계에 실제로 구현될 대상을 의미한다는 말이다. 예시 코드에서는 선언을 통해 아우디, 닛싼, 볼보 등의 객체가 생성되며, 이후 메모리에 할당이 될 때 객체의 인스턴스화가 되는 것을 확인할 수 있다.
인스턴스
클래스를 기반으로 생성된 객체가 메모리에 할당되어 실제 사용될 때
인스턴스라고 부른다. 즉, 이러한 특징을 통해 인스턴스는 객체에 포함된다고 할 수 있다. 인스턴스는 추상적인 개념과 구체적인 객체 사이에 초점을 맞출 경우에 사용되고, 클래스를 기반으로 소프트웨어 세계에 구현된 실체라고 할 수 있다. 예시 코드에서와 같이 아우디, 닛싼, 볼보 등이 실제로 메모리에 할당 되면서 인스턴스화가 진행된다.
클래스 VS 객체
클래스와 객체의 관계에서 클래스가 설계도라 한다면, 객체는 설계도로 구현한 대상을 말할 수 있다.
객체 VS 인스턴스
객체와 인스턴스의 관계에서 객체는 타입으로 선언 되었을 때 객체라고 할 수 있으며, 인스턴스는 객체가 메모리에 할당되어 실제로 사용될 때 인스턴스라고 말할 수 있다.
장점
단점
캡슐 안에 모든 필요한 약 성분들을 담아 내용물이 보이지 않게 하는데 이는 객체 내부에서 필요로 하는 정보를 은닉하는 캡슐화
를 잘 보여주고 있다.절차 지향에서는 요구변경 시 많은 코드의 수정이 필요하다. 그러나 객체지향에서는 클래스로 만들어 모듈화하기 때문에 요구변경에 대한 클래스 안에 메소드만 추가하여 사용자가 추가된 기능을 사용할 수 있게 만들어 이로 인해 코드의 수정이 최소화 된다는 장점이 있다.
객체의 데이터를 외부에서 직접 접근하지 못하게 막고, 함수를 통해서만 조작이 가능하게 하는 작업이다
. 몬스터의 레벨(level)과 같은 경우는 이용자가 임의로 조작하게 하면 문제가 생길 가능성이 있다. 그래서 레벨업(levelUp)이라는 함수를 통해서만 레벨이 조작되어야 한다.코드를 보면 외부에서 level변수에 직접 접근하지 못하도록 막는 캡슐화를 나타내고 있는데 이전의 몬스터 객체처럼 monster.level로 몬스터의 level데이터에 접근할 수 없게 되었다. level은 오직 monster.levelUp이라는 메소드(Method)로만 조작할 수 있게 되는 것이다.
객체의 상태와 행동을 하나의 단위로 묶는 자율적 실체
를 말하고 은닉화는 외부에서 객체의 상태를 변경할 수 없도록 숨기는 것을 말한다
. 캡슐화를 하는 중요한 목적은 바로 정보 은닉인데 이는 필요가 없는 정보는 외부에서 접근하지 못하도록 제한하는 것으로 private 키워드를 사용한다. 이렇게 보호된 변수는 getter나 setter 등의 메소드를 통해서만 간접적으로 접근이 가능하도록 하는 것이 캡슐화의 중요한 목적이다.
그렇다면 정보은닉은 왜 필요할까? 은닉을 하지 않으면 객체의 상태정보에 누구나 접근이 가능해지고 이것은 잘못된 데이터가 들어갈 수 있다는 의미이다. 이를 방지하기 위해 정보은닉은 꼭 필요하게 되는 것이다. 그렇다면 이런 생각이 들 수 있다. '메소드를 사용하더라도 내가 잘못된 데이터를 넣을 수 있지 않을까?' 물론 그럴 수 있지만, 메소드를 사용했기 때문에 잘못된 데이터를 걸러낼 수 있으므로 메소드를 사용하지 않을 때와는 다르게 된다.
추상화의 사전적 의미는 특정한 개별사물과 관련되지 않은 공통된 속성이나 관계 등을 뽑아내는 것이다. 앞에 보이는 페르시안, 스핑크스, 먼치킨은 고양이 종류이다. 이들은 공통적으로 야옹하는 소리를 내고 꼬리가 있고, 다리가 네 개이다. 이를 고양이라는 클래스로 추상화할 수 있다. 이를 컴퓨터의 관점에서 생각해보면 추상화란 데이터나 프로세스 등을 의미가 비슷한 개념이나 표현으로 정의해나가는 과정이면서 동시에 각 개별 개체의 구현에 대한 상세함은 감추는 것
이라고 할 수 있다.
즉, 추상화는 객체들이 가진 공통의 특성들을 파악하고 불필요한 특성들을 제거하는 과정
을 말한다. 객체들이 가진 동작들을 기준으로 이용자들이 동작만 쉽게 구동할 수 있도록 한다. 몬스터 예제에서 레벨업(levelUp)메소드를 실행만 하면 level이라는 속성을 컨트롤 할 수 있었던 것처럼 말이다. 이러한 추상화 과정을 통해 이용자들은 프로그래머가 만든 객체를 더 쉽게 사용할 수 있게 된다.
추상화를 할 때 주의할 점은 속성 위주가 아닌 동작 위주로 정의하는 작업
을 하는 것인데 객체의 동작에 연관이 되지 않은 속성들은 결국 불필요하다. 따라서 불필요한 속성들을 걸러내기 위해 동작을 먼저 정의하고, 동작에 필요한 속성들을 정리하는 것이 좋다.
예를들어, 몬스터들이 공격을 받아 healthPoint가 0이 되면 필드에서 죽는 것(death)으로 처리하고 싶다. 이용자들은 몬스터들을 공격하고 죽는 모습을 보기만 하면 되고 어떠한 과정을 거쳐서 몬스터가 죽었는지에 대해서는 알 필요가 없다. 이 상황에서는 앞의 문스터 객체에 2가지 속성이 추가되어야 하는데 몬스터가 죽었는지 살았는지를 확인하는 속성과 healthPoint라는 속성이다.
이제 몬스터는 체력(healthPoint)를 가지고 있고 데미지를 받아 체력이 깎이다가 체력이 0이 되면 죽는다. 이렇게 어떠한 동작과 속성을 정의하고 불필요한 정의들을 삭제하여 이용자가 편리하게 객체를 이용할 수 있도록 구성한 것이 추상화다.
상속은 객체지향의 꽃이라고 할 수 있는데 이는 기존 상위클래스에 근거하여 새롭게 클래스와 행위를 정의할 수 있게 도와주는 개념이다. 즉 기존 클래스에 기능을 가져와 재사용할 수 있으면서도 동시에 새롭게 만든 클래스에 새로운 기능을 추가할 수 있게 만들어준다
. 예를들어 동물이라는 클래스의 속성을 강아지 클래스 또는 고양이 클래스가 물려받을 수 있다.
상속이 필요한 이유는 코드의 중복을 막을 수 있어 코드가 더 간단해지므로 유지보수가 수월해지기 때문이다. OOP에서는 상속을 통해 코드의 중복 문제를 일부 해결할 수 있는데 동물 클래스에 여러 속성들을 정의해놓고 동물에 해당하는 종, 예를들면 강아지 클래스가 필요한 경우 동물 클래스와 상속 관계를 맺는다. 상속 관계를 맺으면 자식 객체를 생성할 때 부모 클래스의 속성들을 자동으로 물려받기 때문에 자식 클래스에서 또 정의할 필요가 없기 때문이다.
다형성은 상속을 통해 기능을 확장하거나 변경하는 것을 가능케해준다. 즉 형태가 같은데 다른 긴으을 하는 것을 의미한다. 이를 통해 코드의 재사용, 코드가 짧아져 유지보수가 용이하도록 도와준다.
예를들면 고양이 클래스에는 울음이라는 속성이 정의되어 있다고 하자, 사자는 고양이 과이기 때문에 사자 클래스가 고양이 클래스를 상속받는다고 하면, 사자 클래스에도 '울음'이라는 속성이 자동으로 추가된다. 그런데 고양이와 사자의 울음소리는 다르다. 같은 '울음' 속성임에도 실제 울음소리는 다르다. 바로 이런 것을 다형성이라고 말할 수 있다.
OOP에서 다형성의 개념을 녹여내는 방법은 두 가지가 있다. 바로 오버라이딩과 오버로딩이다.
오버라이딩은 부모 클래스에서 상속받은 자식 클래스에서 부모 클래스에서 만들어진 메소드를 자식 클래스에서 자신의 입맛대로 다시 재정의해서 사용
하는 것을 말한다.
위의 코드에서 employee라는 클래스에서는 이름과 나이를 속성으로 가지고 있고 이를 상속받는 매니저 클래스에서는 employee 클래스에 있던 이름과 새로운 속성인 jobOfManagement를 이용해 재정의하여 사용한다. 따라서 위의 경우 사원의 이름 홍길동, 나이 30이고 아래는 관리자 홍길동은 영업담당입니다. 라는 결과가 나오게 된다.
오버로딩은 같은 이름의 메소드를 사용하지만 메소드마다 다른 용도로 사용되며 그 결과물도 다르게 구현할 수 있게 만드는 개념
이다. 오버로딩이 가능하려면 메소드끼리 이름은 같지만 매개변수의 갯수나 데이터 타입이 달라야 한다.
위 코드에서 이름이 test인 메소드가 총 세 개가 있지만 각각 매개변수의 유형과 갯수가 다른 것을 볼 수 있는데 차례대로 매개변수가 없는 test, 매개변수가 string형인 test, 그리고 매개변수가 string형과 int형을 가지고 있는 test이다. 호출 시 매개변수를 입력하면 호출 매개변수에 따라 매칭되어 함수를 실행시켜 준다. 따라서 결과는 사용자 없음, 사용자 이름은 홍길동, 사용자 이름은 홍길동 사용료는 5000이 되는 것이다.
그럼 다형성을 사용하면 어떤점이 좋은가? 하면 같은 이름의 속성을 유지함으로서, 속성을 사용하기 위한 인터페이스를 유지하고, 메소드 이름을 낭비하지 않는다
는 것이다. 예를들어 앞서 나왔던 고양이와 사자의 울음소리를 호출하기 위해서 각 객체에서 roar()이라는 메소드를 호출하면 된다. roarCat(), roarLion()으로 각각을 정의할 필요가 없다는 것이다. API가 많아질수록 복잡성을 증가하기 때문에 다형성은 유용하다고 할 수 있다.