-
객체지향 프로그래밍 & 특징Mhwan's Study/Architecture & Design Pattern 2021. 7. 29. 18:49
객체지향 언어의 특징은 캡슐화, 상속, 다형성, 추상화가 있습니다.
솔직히 저는 그동안 면접용을 위해 이 4개의 특징이 무엇인지 달달 외웠던 것 같고, 실제로 무슨 특징을 의미하는 것인지는 몰랐습니다. 그래서 디테일하게 객체지향 언어가 무엇인지 공부를 해보았고, 포스팅하게 되었습니다.
# 절차지향 프로그래밍?
프로그램을 구현하게 되면 그 안에는 데이터와 데이터를 조작하는 함수나 프로시저로 이루어져있습니다. 프로시저는 다른 프로시저를 사용할 수도 있고, 각각의 프로시저가 같은 데이터를 사용할 수도 있는데, 이러한 프로시저가 중심이 되어 프로그램을 구성하는 기법을 절차지향 프로그래밍이라고 합니다.
절차지향 언어의 문제는 기존의 프로그램을 수정할 때 생기게 됩니다. 데이터 중심적으로 프로그램을 짜게 된다면, 데이터 타입 등 데이터가 바뀌게 된다면 그걸 사용하는 함수나 프로시저를 모두 수정해줘야합니다. 즉, 새로운 변경사항이 있을 때마다 한곳만 수정하는게 아니라 다른 곳을 다 수정해줘야하는 악순환이 생기는 문제가 있습니다.
# 객체지향 프로그래밍?
객체 지향 언어는 바로 객체라는 개념으로 데이터와 프로시저를 묶게 됩니다. 객체는 프로시저를 실행하는데 필요한 데이터만을 갖고 있는데, 이렇게 되면 해당 객체의 데이터를 바꾸어도 변화는 해당 객체에만 집중되고 다른 객체에 영향을 주지 않게 됩니다. 이렇게 객체지향 프로그래밍은 절차지향 프로그래밍에 비해 요구사항 변경에 더 유연하게 대처할 수 있는 장점을 갖고 있습니다.
캡슐화
정보은닉 (필요가 없는 정보는 외부에서 접근하지 못하도록 제한)
즉, 객체 내부적으로 기능 구현을 외부에 감춞으로써 객체 내부의 기능 구현이 바뀌어도 객체 외부에 미치는 영향을 최소화합니다. 내부 구현 변경의 유연함을 주는 기법입니다.
이를 위해서는 '객체가 어떤 데이터를 주고 받는가'보다는 '객체간의 어떤 메시지를 주고 받는가'에 집중해야하며 '디미터의 법칙'을 지켜 코드를 짜야합니다. (디미터의 법칙에 대해서 포스팅하지는 않겠습니다.)
123456789101112131415//데미테르 법칙을 위반한 코드//신문 배달부 입장에서는 단순히 고객으로부터 요금만 받으면 된다. (굳이 지갑을 뒤질필요가 없다)class Paperboy {public void takeCharge(Customer customer) {int payment = 10000;Wallet wallet = customer.getWallet();if(wallet.getTotalMoney() >= payment) {wallet.subtractMomey(payment);} else {// 다음에 요금 받으러 오는 처리}}}cs 위 코드는 고객이 돈을 갖고 있다면 돈을 받는 행동인데, 신문 배달부 입장에서 필요없는 고객의 지갑까지 뒤져 고객이나 지갑의 변경이 신문배달부의 변경까지 야기시키게 될 것입니다.
디미터의 법칙을 적용시키면 아래와 같이 변경할 수 있습니다.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748class Customer {private Wallet wallet;public int getPayment(int payment) {if(wallet == null) throw new NotEnoughMoneyException();if(wallet.getTotalMoney() >= payment) {wallet.subtractMomey(payment);return payment;}throw new NotEnoughMoneyException();}}/*** 지갑*/class Wallet {private int money;public int getTotalMoney() {return money;}public void subtractMomey(int debit) {money -= debit;}}class Paperboy {public void takeCharge(Customer customer) {int payment = 10000;try {int paidAmount = customer.getPayment(payment);...} catch (NotEnoughMoneyException ex) {// 다음에 요금 받으러 오는 처리}}}cs 상속
한 타입을 그대로 사용하면서 구현 사항을 추가할 수 있도록 하는 방법, overriding을 통해 하위 클래스는 필요에 따라 상위 클래스에 정의된 메소드를 새롭게 구현, 기능을 확장할 수 있습니다. (단, LSP(리스코프 치환의 원칙)에 따라 하위 객체에서 상위 객체의 명세를 벗어나게 하면 안됩니다.)
다형성
한 객체가 여러 타입을 가질 수 있으며, 객체가 같은 메시지를 받았을 때 각자에게 맞는 방식으로 동작하는 것을 의미합니다. (오버라이딩, 오버로딩)
즉, 타입 A, B, C라고 정의된 인터페이스를 구현하는 객체는 타입 A로도 사용, B로도 사용, C로도 사용될 수 있다는 것을 말하기도 합니다.
- 오버로딩 (Overloading) : 하나의 클래스에서 같은 이름의 여러 메소드를 가질 수 있게 합니다. 단 메소드의 파라미터가 달라야하며 반환 타입은 같아도 되고, 달라도 됩니다. 파라미터 타입이나 갯수가 다르면 메소드 이름이 같아도 어떤 메소드를 호출할지 컴파일러가 알 수 있기 때문입니다. (오버로딩은 대상이 되는 메소드를 컴파일 시점에 정하게 됩니다.)
- 오버라이딩 (Overriding) : 부모 클래스를 상속 받은 서브 클래스에서 부모 클래스의 메소드 (같은 메소드 이름, 같은 반환 값, 같은 파라미터)를 새롭게 정의하는 것입니다. 이를 이용해 하나의 부모클래스를 상속받는 여러 자식 클래스들이 같은 이름에 다른 기능을 하는 메소드를 정의해 사용할 수 있습니다. (오버라이딩은 대상이 되는 함수를 런타임 시점에 정의하게 됩니다.)
추상화
공통된 기능이나 특징을 하나의 집합으로 모으는 것을 의미합니다.
추상화된 타입은 구체적인 실제 구현을 제공하는 것이아니라 의미만 전달합니다. 따라서 추상타입과 실제 구현 클래스는 상속을 통해 연결되게 됩니다. (추상타입에 대한 실제 구현 클래스는 concrete class라고 부릅니다.)
추상화를 통해 공통 개념을 추출해 추상 타입을 정의하기도 하며, 많은 책임을 가진 객체로 부터 책임을 분리할 수 있습니다. 즉, 추상화를 통해 추상 타입을 사용하는 코드에는 영향을 주지 않으면서, 추상 타입의 실제 구현 클래스만을 변경함으로써 유연함을 높일 수 있게 됩니다.
하지만, 모든 경우에 추상화를 위해 인터페이스를 사용하게 된다면 불필요하게 프로그램 복잡도가 증가되기 때문에, 변경 가능성이 높은 경우에 한해 사용하는게 좋습니다.
'Mhwan's Study > Architecture & Design Pattern' 카테고리의 다른 글
[Android] MVC VS MVP VS MVVM (+ SVC) (0) 2021.08.12 MVC VS MVP VS MVVM (0) 2021.08.04