ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • OOP 개념잡기
    카테고리 없음 2017. 3. 2. 13:37



    개요


    객체지향 프로그래밍(Object-Oriented Programming), 줄여서  OOP, 프로그램을 어떻게 설계해야 하는지에 대한 일종의 개념이자 방법론.


    - 상세절차

    프로그램을 단순히 데이터와 처리 방법으로 나누는 것이 아니라, 프로그램을 수많은 '객체'라는 기본 단위로 나누고 이 객체들의 상호작용으로 서술하는 방식이다.


    - 절차적 프로그래밍과 비교

    기본 절차적 프로그래밍에서는 함수를 기계, 데이터를 원료로 생각해서 데이터가 함수 사이를 통과하면서 차츰 순서대로 가공돼 나가는 방식으로 이해한다면, 객체 지향 프로그래밍에서는 데이터를 중심으로 메서드가 데이터에 접근해서 수정한다는 개념이다. 즉 원료가 움직이냐 기계(함수)가 움직이냐의 차이다. 




    시작과 발전


    - 절차적 프로그래밍

    초기 프로그래밍 방식은 절차적 프로그래밍 방식이었다. 입력을 받아 명시된 순서대로 처리한 다음, 그 결과를 내는 것뿐이라는 생각이 지배적이었다. 프로그램을 명령어의 모음으로 인식한 것이다. 또한 프로그래밍이란 어떻게 어떤 논리를 순서대로 써나가는 것인가로 간주되었다. 즉, 프로그램 자체가 가지는 기능에 대해서만 신경을 썼지, 이 프로그램이 대체 어떤 데이터를 취급하는 것인가에는 그다지 관심이 없었던 것이다. 


    - 절차적 프로그래밍의 문제점 

    간단한 알고리즘이면 모를까 조금만 복잡해져도 순서도로 나타내는 것이 불가능할 정도로 꼬인 스파게티 코드가 나오게 된다. 또한 작성한 본인조차 유지보수에 어려움을 겪을 수 있으며, 이렇게 꼬인 코드를 다른 사람이 보고 이해하는 것은 불가능하다.


    - '구조적 프로그래밍' 을 제안 

    이 문제를 해결하기 위해 프로그램을 함수(프로시져) 단위로 나누고 프로시져끼리 호출을 하는 구조적 프로그래밍 방식을 제안하면서 이러한 위기를 벗어난다. 프로그램이라는 큰 문제를 해결하기 위해 그것을 몇 개의 작은 문제들로 나누어 해결하기 때문에 하향식(Top-Down) 방식이라고도 한다. 


    - 불완전한 구조화의 문제점

    함수는 데이터의 처리 방법을 구조화했을 뿐, 데이터 자체는 구조화하지 못했다. 전역 네임스페이스 포화 문제를 낳게 되었다. 게다가 실행 콘텍스트를 저장할 마땅한 방법이 없어지는 문제가 생겼다. 또한 엉뚱한 데이터가 엉뚱한 함수에 전달돼서 데이터를 오염시키는 문제가 발생하고 그런 가능성 때문에 프로그래머가 한 함수의 거동에 영향을 받는 변수를 조사해야 할 때 모든 변수를 다 조사해야 하는 어려움에 봉착했다.


    - 객체 지향 프로그래밍의 등장

    이를 극복하기 위한 대안으로 등장한 것이 바로 객체 지향 프로그래밍이다. 큰 문제를 작게 쪼개는 것이 아니라, 먼저 작은 문제들을 해결할 수 있는 객체들을 만든 뒤, 이 객체들을 조합해서 큰 문제를 해결하는 상향식(Bottom-Up) 해결법을 도입한 것이다. 이 객체란 것을 일단 한번 독립성/신뢰성이 높게 만들어 놓기만 하면 그 이후에 그 객체를 수정 없이 재사용할 수 있으므로 개발 기간과 비용이 대폭 줄어들게 된다.


    객체지향 프로그래밍은 등장 당시에는 기존의 절차적 프로그래밍과 비교해 매우 이질적이고, 당시 컴퓨터의 처리능력이 좋지 않아서 별 주목을 받지 못하였다. 


    - GUI의 등장

    그러다가 GUI 등장하면서 객체 지향 프로그래밍이 급부상하게 된다. 화면에 떠 있는 여러 개의 창은 각자의 실행 콘텍스트를 가지는데 콘텍스트의 현재 상태(활성화, 비활성화, 최소화)에 따라 같은 명령에도 다른 결과를 내보내야 했으며 사용자 상호작용을 위해 이벤트 처리도 수행해야 했다. 특히 이벤트 처리는 비동기적인 속성 때문에 기존 절차적 프로그래밍에서는 일종의 횡단 관심사가 되어 버려 코드 전체에 이벤트 처리 코드가 흩어져 있게 되는 문제가 있었다. 그래서 OOP를 도입하여 이벤트를 받았을 때 수행되는 기능(Event Handler, Callback)을 구현할 수 있는 단일 인터페이스를 정의하고, 프로그래머들은 이를 필요한 형태로 알아서 구현하며, 특정 이벤트가 일어났을 때 실행 되어야 하는 기능들을 등록한 다음, 운영체제나 응용프로그램이 실제로 해당 이벤트가 발생했을 때 해당 이벤트에 등록된 이벤트 핸들러/콜백을 주욱 실행하기만 하면 되는 구조가 본격적으로 확산되면서 OOP 또한 빠르게 확산되었다. 




    캡슐화


    - 캡슐화의 목적은 코드를 재수정 없이 재활용 하는 것

    객체 지향 방식의 기본중의 기본 개념이다. 이 캡슐화를 안지키면 나머지 상속과 다형성은 성립이 안된다. 캡슐화의 목적은 "코드의 수정 없는 재활용"을 생각해보면 당연한 개념이다. 프로그램 코드를 재활용 하려고 하는데 기능(함수/프로세저)이 분산되어 있고 특성(변수:데이터)이 분산되어 있는 프로그램 코드는 재활용을 하기 매우 힘들다. 이 때문에 관련 기능과 특성을 한곳으로 모으고 분류할 필요성이 있다.


    - 클래스 & 인스턴스 

    객체 지향에서는 이렇게 계층적으로 분류한 기능과 특성의 모음을 클래스(Class)라는 캡슐(capsule)에 분류된 집단 별로 각각 집어 넣는다. 이러한 클래스를 실체화(Instance) 하면 객체(Object)를 만들 수 있다.


    - 컴퓨터 내부 처리 절차

    객체 생성(생성자) : 실제 컴퓨터에서 물리적으로 일어나는 현상은 "홍길동"이라는 객체 생성을 위해 "한국인 Class"에 정의된 데이터가 들어갈 메모리를 힙에 할당 하고 기본적인 한국인 클래스의 특성을 초기화 하기 위해 생성자를 호출하여 "홍길동" 객체를 초기화한다.


    객체 소멸(소멸자) : 객체를 메모리에서 제거할 땐 할당된 자원(메모리, 파일 등)을 정리하기 위해 소멸자(Destructor)를 호출한다. 소멸자의 경우 Java나 .NET Framework 같이 가비지 콜렉션이 있는 객체 지향 플랫폼일 경우에는 내부적으로 사용하므로 특별한 경우 외에는 소멸자를 프로그램 코드에 넣지 않는다. 


    - 정리 

    이와 같이 캡슐화는 '클래스, 타입, 인스턴스, 생성자, 생성자, 소멸자' 같은 객체 지향 프로그램 방식의 기초를 형성한다. 다음에 설명할 상속성과 다양성은 객체의 메커니즘(작동 원리)이기 때문에 캡슐화를 정확히 표현하지 못하면 상속성과 다형성 또한 표현을 잘못하게 되어 있다.




    상속(Inheritance)


    사실 클래스 이전의 프로그래밍 기법에선 코드 재활용이 불가능하지는 않았다. 예전에도 라이브러리 등을 통해서 남이 짜놓은 코드를 그대로 가져올 수 있었다.


    - '라이브러리' 의 문제점

    라이브러리는 코드의 재활용에 지대한 영향을 미쳤지만 치명적인 단점이 있었다. 라이브러리의 기능을 약간 바꾸어야 할 경우 라이브러리의 소스를 변경해야 했고 이 때문에 전혀 다른 라이브러리가 되어버린다는 것이다. 이것은 라이브러리 버전에 따라 그 라이브러리를 사용하는 프로그램을 동작을 안할수도 있다는 것이고 불필요한 코드의 수정작업을 해야 한다는 것이다.


    - '상속' 의 도입

    객체 지향 프로그램에서는 이 문제를 해결하기 위해 "상속"을 도입 했다. "포용성"으로 이전의 라이브러리보다 더 논리적이고 체계적으로 기능과 데이터를 계층적으로 분류해서 사용의 편의성을 도모하면서, 상속을 사용해 부모 클래스의 특성과 기능을 그대로 이어받고 기능의 일부분을 변경해야 할 경우 상속 받은 자식 클래스에서 그 기능만을 다시 정의하여 수정하게 하였다. 이러한 작업을 "덮어쓰기(재정의:Override)"라고 한다.




    다형성(Polymorphism)


    하나의 변수명, 함수명 등이 상황에 따라 다른 의미로 해석될 수 있다는 것을 말한다. 위에서 설명한 오버라이딩 이외에, 변수에 따라 함수의 기능이 달라지는 오버로딩도 여기에 해당한다.


    - 연산자 오버로딩

    C++, C# 등에서는 기본 연산자를 오버로딩해서 기본 연산자가 해당 클래스에 맞게 역할을 수행하게 하는 것도 가능하다.




    장단점


    - 데이터 클래스 개념은 상속이라는 굉장히 뛰어나지만 마찬가지로 굉장히 개 같은 특성을 지니게 해준다. 이 OOP 특성 덕분에 면밀한 자료 분석, 개발 시간 단축, 좀 더 정확한 코딩을 보증하지만 코드의 난이도가 급 상승한다. 한 마디로 어려워진다. 특히 다중 상속이 되면 엄청 복잡해진다. 그래서 현재에는 다중 상속을 지원하지 않는 OOP 언어가 대부분이며, 다중 상속이 되는 C++ 같은 것도 다중 상속은 최대한 자제하라고 권장하고 있다. 


    - 클래스는 오로지 관련 데이터만을 정의하기 때문에, 한 클래스의 인스턴스가 수행될 때 다른 프로그램의 데이터를 절대로 건드릴 수 없게 된다. 덕분에 높은 시스템 보안을 제공하고, 자료 훼손을 방지하는 효과가 있다. Visual Studio 6.0의 C++는 데이터 베이스의 자료 처리는 변수를 모두 public 으로 처리 했었다. 그 이유는 데이터베이스의 필드가 50개이면 get과 set 메소드를 모두 구현해주면 100개의 메소드를 구현해줘야 한다. 변수 50개와 메소드 100개다. 150개를 키보드로 쳐야 한다. 생각만 해도 일할 기분이 안난다. 이건 Java 도 마찬가지다. 변수의 get 과 set 메소드 구현은 어쨋던 굉장히 귀찮은 작업이다. 




    기타


    참고로, 최근 주목을 받고있는 함수형 패러다임과는 다소 상반된 위치에 있다. OOP의 경우, 프로그램의 유지보수시 데이터 추가는 새로운 클래스를 더하는 것으로 비교적 간단하게 가능하지만, operation set 을 변경할 때는 관련된 다수 클래스를 수정해야 하므로 난잡해지는 경향이 있다. 반대로, 함수형 패러다임에서는 operation set 의 추가는 간단하지만, 데이터 추가는 관련된 다수의 함수를 바꿔야 하므로 난해한 점이 있다. 주의할 점은 OOP와 함수 패러다임이 상반된 위치에 있긴 하지만, 대비되는 개념은 아니며, 요즘에는 함수형 언어에도 OOP 개념을 추가하던가(F#), 반대로 객체지향언어에 함수형 패러다임을 추가하는(C#, C++, Python 등...) 등 멀티패러다임 추세로 가고있다. 


    클래스가 있어야만 객체 지향 프로그래밍 언어라고 생각할 수도 있지만, 사실 클래스 없는 OOP 언어도 꽤 있다. 프로토타입을 이용하는 JavaScript, 액션스크립트 2.0, 타입 클래스라는 개념을 쓰는 Haskell, 그냥 함수만 써서 다 해먹는 LISP 등의 각종 함수형 언어 등등...









    OOP 특성


    1) 추상화(Abstraction)

    - 공통의 속성이나 기능을 묶어 이름을 붙이는 것

    - 객체 지향적 관점에서 클래스를 정의하는 것을 바로 추상화라고 정의 내릴 수 있겠다.

    - 좀 더 살펴보면 물고기, 사자, 토끼, 뱀이 있을때 우리는 이것들을 각각의 객체라 하며 이 객체들을 하나로 묶으려 할 때, 만약 동물 또는 생물이라는 어떤 추상적인 객체로 크게 정의한다고 하자. 이때 동물 또는 생물이라고 묶는 것을 추상화라고 한다.



    2) 캡슐화(Encapsulation)

    - 데이터 구조와 데이터를 다루는 방법들을 결합 시켜 묶는 것, 다시 한번 말하자면 변수와 함수를 하나로 묶는 것을 말한다.


    public String ()test {

        string aa = "aaa";

    }


    - 하지만 무작정 묶으면 되는 것이 아니라 객체가 맡은 역할을 수행하기 위한 하나의 목적을 한데 묶는다고 생각해야 한다. 이것이 많이 들어본 의미로 은닉화라고 한다.

    - 또한 데이터를 절대로 외부에서 직접 접근을 하면 안되고 오로지 함수를 통해서만 접근해야하는데 이를 가능하게 해주는 것이 바로 캡슐화이다.

    - 따라서 캡슐화에 성공하면 당연히 은닉화도 자연스럽게 효력이 나타난다. 



    3) 상속성, 재사용셩(Inheritance)

    - 상속 개념의 특징을 하위 개념이 물려받은 것

    - 객체지향의 하이라이트 부분이라고 생각한다. 상속이란 개념이 없으면 객체지향이나 절차지향이나 도진개진

    - 간단한 예를 들자면, 자동차라는 부모 클래스가 있다. 

    기름을 먹거나 달리는 기능을 하는 자동차인데, 만약 지붕뚜껑이 열리는 특수한 기능을 추가하고 싶다면 기존의 자동차에서 스포츠카를 생성한다.

    그러면 스포츠카는 기름도 먹고 달리면서 지붕뚜껑이 열리는 기능도 갖춘 자동차가 되는 것.



    4) 다형성(Polymorphism)

    - 부모클래스에서 물려받은 가상 함수를 자식 클래스 내에서 오버라이딩 되어 사용하는 것

    - 간단한 예를 들자면 

    군대에서 나는 K2 소총을 잡았고 동기는 K1 소총을 잡았다. 중대장이 '발사'라고 외치면 명령을 받고 총을 쏜다. 이때 중대장은 추상적 객체를 상속받은 모든 객체들에게 명령을 내린것이고 병사의 총이 뭐든간에 그냥 발사를 하라는 명령을 한 것이다.

    즉, 다형성이 없다면 K1 소총을 든 병사 발사, K2 소총을 든 병사 발사라며 명령을 하나하나 내려야 할 것이다. 







Designed by Tistory.