-
절차지향 VS 객체지향공부 2018. 12. 22. 18:35
절차지향(Procedural Programming)이란?
절차지향 프로그래밍이란 물이 위에서 아래로 흐르는 것처럼 순차적인 처리가 중요시 되며 프로그램 전체가 유기적으로 연결되도록 만드는 프로그래밍 기법입니다. 대표적인 절차지향 언어에는 C언어가 있습니다. 이는 컴퓨터의 작업 처리 방식과 유사하기 때문에 객체지향 언어를 사용하는 것에 비해 더 빨리 처리되어 시간적으로 유리합니다. 옛날에는 하드웨어와 소프트웨어의 개발 속도차이가 크지 않았습니다. 하지만 하드웨어의 빠른 발전을 통해 컴퓨팅 환경은 급속도로 증가했지만 소프트웨어 개발 시간이 따라가지 못하게 되고 이런 상황에 소프트웨어의 개발시간을 단축하되 하드웨어에 기본적인 사양을 잡아먹어도 더 이상 큰 단점이 아니기에 모듈화, 캡슐화해서 개념적으로 접근하는 형태를 갖는 객체지향 프로그래밍이 탄생했습니다 (디아이라님 감사합니다). 객체지향 프로그래밍은 개발하려는 것을 기능별로 묶어 모듈화를 함으로써 같은 기능을 중복으로 연산하지 않거나 모듈을 재활용하기 때문에 유지보수에 유리합니다.
장점
- 컴퓨터의 처리구조와 유사해 실행속도가 빠름
단점
- 유지보수가 어려움
- 실행 순서가 정해져 있으므로 코드의 순서가 바뀌면 동일한 결과를 보장하기 어려움
- 디버깅이 어려움
객체지향(Object Oriented Programming)이란?
객체지향의 정의를 살펴보면 객체지향이란 실제 세계를 모델링하여 소프트웨어를 개발하는 방법입니다. 객체지향 프로그래밍에서는 데이터와 절차를 하나의 덩어리로 묶어서 생각하게 됩니다. 이는 마치 컴퓨터 부품을 하나씩 사다가 컴퓨터를 조립하는 것과 같은 방법입니다. 객체 지향의 3대 특성은 다음과 같습니다.
1. 캡슐화
- 캡슐화란 관련된 데이터와 알고리즘(코드)이 하나의 묶음으로 정리된 것으로써 개발자가 만들었으며, 관련된 코드와 데이터가 묶여있고 오류가 없어 사용이 편리합니다. 데이터를 감추고 외부 세계와의 상호작용은 메소드를 통하는 방법인데, 라이브러리로 만들어 업그레이드하면 쉽게 바꿀 수 있습니다.
- 메소드: 메시지에 따라 실행시킬 프로시저로서 객체지향 언어에서 사용되는 것. 객체지향 언어에서는 메시지를 보내 메소드를 수행시킴으로써 통신(communication)을 수행
2. 상속
- 상속은 이미 작성된 클래스를 이어 받아서 새로운 클래스를 생성하는 기법으로 위에서 말한 기존 코드를 재활용해서 사용하는 것을 의미합니다. 객체지향 방법의 큰 장점중 하나입니다.
3. 다형성
- 다형성이란 하나의 이름(방법)으로 많은 상황에 대처하는 기법입니다. 개념적으로 동일한 작업을 하는 함수들에 똑같은 이름을 부여할 수 있으므로 코드가 더 간단해지는 효과가 있습니다.
위의 특성들로 인해 생기는 객체지향 방법의 장점은 다음과 같습니다.
- 신뢰성 있는 소프트웨어를 쉽게 작성할 수 있다. (개발자가 만든 데이터를 사용하기에 신뢰할 수 있다.)
- 코드를 재사용하기 쉽다.
- 업그레이드가 쉽다.
- 디버깅이 쉽다.
이론적으로만 본다면 객체지향 언어는 절차지향 언어에 비해 장점이 많습니다. 하지만 프로그래밍을 할 때 항상 객체지향 언어만 사용하는 것은 아닙니다. 객체지향 언어는 어떤 모듈에 있는 하나의 기능만 필요하더라도 모듈 전체를 가져와야 하기 때문에 절차지향 프로그래밍보다 프로그램 사이즈가 더 커질 수도 있습니다. 또한 데이터에 대한 접근도 상대적으로 절차지향식보다 느려질 가능성이 많습니다. 메소드를 통해서만 접근이 가능하기 때문에 절차지향식처럼 특정 함수에 접근할 수 없고, 식으로만 접근이 가능해 속도적인 측면에서 불이익이 있습니다.
장점
- 코드의 재활용성이 높음
- 코딩이 절차지향보다 간편함
- 디버깅이 쉬움
단점
- 처리속도가 절차지향보다 느림
- 설계에 많은 시간소요가 들어감
객체지향과 절차지향의 차이점
객체지향의 반대는 절차지향이 아니고 절차지향의 반대는 객체지향이 아닙니다. 위에서 설명한 것처럼 절차지향은 순차적으로 실행에 초점이 되어 있고 객체지향은 객체간의 관계/조직에 초점을 두고 있습니다. 이렇게 설명하면 객체지향은 절차적으로 실행되지 않냐? 라는 의문이 드는데 객체지향 역시 절차지향과 동일한 순서로 실행됩니다.
절차지향은 데이터를 중심으로 함수를 구현합니다. 이에 반해 객체지향은 기능을 중심으로 메서드를 구현하게 됩니다.아래는 절차지향과 객체지향의 차이점 예시입니다.someServiceCheck() { ... if (new Date().after(member.getExpirationDate()))) { ... } ... } // Member의 일부 private Date expirationDate; public Date getExpirationDate() { return expirationDate; } public void setExpirationDate(Date expDate) { expirationDate = expDate; }
위 코드는 만료 여부를 확인하기 위해 member.getExpirationDate()로 만료일을 구합니다. 즉, someServiceCheck() 메서드는 member의 expirationDate 데이터를 사용하고 있습니다. 현재 시점에서 expirationDate 데이터는 someServiceCheck()와 Member가 공유하고 있습니다.
여기서 만료일을 1년 늘려주는 코드는 아래와 같이 구현됩니다.
renewContract() { Date date = member.getExpirationDate(); Date renewedDate = ... // date에 1년 더한 값 member.setExpirationDate(renewedDate); }
이제 Member의 expirationDate를 공유하는 함수는 someServiceCheck()와 renewContract()로 늘어났습니다. 여기서 Member는 객체가 아닙니다. Member는 객체라기 보다는 데이터를 담고 있는 구조체에 가깝습니다. 즉, 두 함수가 데이터를 공유하고 이를 기준으로 구현하는 전형적인 절차지향 방식입니다.
만료 여부를 확인하는 코드가 많아지거나 만료 데이터를 변경하는 코드가 많아질수록 expirationDate라는 데이터를 중심으로 함수를 구현하게 됩니다.
이렇게 절차 지향은 데이터를 중심으로 코드를 구현합니다. 개별 함수가 일부 기능을 구현하지만, 그 기능의 완성은 데이터 공유에 있습니다.절차 지향은 데이터를 중심으로 연관된 함수들을 묶어줍니다. 여기에 발생될 수 있는 문제점으로 예를 들어, 만료 없이 무한정 서비스를 받을 수 있다는 것을 표현하기 위해 expirationDate에 null을 할당하기로 했다고 가정합니다. 이 순간 데이터를 공유하는 모든 함수가 영향을 받습니다. 기존 코드에 null 검사를 추가해야 하고, null이면 에러가 아니라 만료일이 없도록 로직을 수정해야 되고 null 대신 9999년 12월 31일을 만료일자로 주기로 했다고 가정하면 남은 기간을 중심으로 환불 금액을 구하는 refund() 함수와 기타 만료일을 중심으로 중요 로직을 수행하는 코드들이 영향을 받게 됩니다.
객체 지향은 데이터 구조체가 아닌 기능을 중심으로 프로그램이 엮이게 됩니다. 예를 들어, Member를 구조체가 아닌 기능을 제공하는 객체로 바꾸면 다음과 같이 생성됩니다.
public class Member { private Date expirationDate; public boolean isExpired() { return new Date().after(expirationDate); } public int getRestDay() { ... // } public boolean renewContract() { // 데이터와 관련된 일부 기능이 객체로 들어옴 .... // } }
이제 다른 기능들은 만료 여부를 확인하기 위해 expirationDate 데이터를 사용하지 않고 Member가 제공하는 isExpired() 메서드를 사용합니다.
someServiceCheck() { ... if (member.isExpired())) { ... } ... }
계약 갱신 기능과 같이 일부 기능은 Member 안으로 들어가게 됩니다. 데이터와 밀접하게 연결된 기능을 데이터와 같은 객체의 기능으로 넣습니다. 이렇게 함으로써 객체의 내부 구현(특히 데이터)를 외부에 노출하지 않을 수 있습니다. 이를 캡슐화라 부릅니다.
기능 구현을 캡슐화하면 내부 구현 변경을 조금 더 쉽게 할 수 있습니다. Member 객체를 사용함으로써 만료 여부 로직을 변경할 때 다른 코드는 영향을 받지 않게 됩니다. 무한대로 사용할 수 있는 사용자의 만료 데이터를 null로 하든, 9999년 12월 31일로 하든, Member 객체를 사용하는 코드는 isExpired() 라는 기능을 사용하면 되기 때문입니다. 만료 데이터 저장 방식 때문에 영향을 받는 코드는 Member 객체 뿐이므로 변경이 수월해집니다.
자바나 C#과 같은 언어가 아니라 C와 같은 언어를 사용해도 객체 지향적으로 코딩할 수 있습니다. 핵심은 데이터 중심이 아닌 기능 중심으로 구현하는 것입니다. 즉, 여러 함수가 데이터를 공유하는 방식이 아니라 특정 함수가 다른 함수를 사용하는 방식으로 구현을 하고, 데이터 공유를 적절히 제한하면 캡슐화 효과를 얻을 수 있습니다.
요약
- 절차지향은 데이터 중심, 객체지향은 기능 중심
- 절차지향의 반대는 객체지향이 아니고 객체지향의 반대는 절차지향이 아님