로빈의 개발로그
CS - 객체지향 프로그래밍(OOP) 본문
절차지향 프로그래밍이란?
객체지향 프로그래밍을 살펴보기전 "절차지향" 프로그래밍을 간단하게 설명하면, 객체지향에 대한 이해가 보다 쉬울 것 같아서 간단하게 짚고 넘어가려 한다.
절차지향은 말 그대로 순차처리로 진행하는 프로그래밍 체계이며, 코드의 시작점부터 아래로 흐를 수록 말그대로 코드가 실행되는 것이다. 이는 컴퓨터의 작업 방식과도 유사하며, C언어가 절차지향 언어로 진행하는 언어라고 볼 수 있다.
객체지향은 이러한 프로그래밍 방법과 다르게 객체를 중심으로 한 프로그래밍 체계를 만든것이다. 그렇다면 왜 다른 프로그래밍 패러다임이 생긴걸까?
하드웨어의 발전과 소프트 웨어의 발전 속도의 차이로, 소프트웨어의 개발시간을 단축 할 수 있는 방법론에서 객체지향이 탄생했다.
객체지향 프로그래밍이란?
객체지향 프로그래밍(Object-oriented programming)은 객체라는 개념에 실제 세계를 모델링하여 소프트웨어를 개발하는 체계이다. 객체에는 데이터나 코드를 담을 수 있으며, 객체들은 유기적으로 프로그램내에서 상호작용 할 수 있다.
객체는 속성(property) + 기능(function)으로 이루어진다.
일례로 자동차를 객체라고 본다면, 자동차의 색상, 브랜드, 출력 등은 프로퍼티라 정의할 수 있으며, 자동차의 행동인 주행, 후진, 변속 등등은 기능으로 볼 수 있다.
객체지향 프로그래밍의 장점
객체지향 프로그래밍의 장점은 바로 "재사용성"에 있다. 하드코딩을 하게되면 각 객체의 속성이나 기능을 일일히 업데이트 해줘야 한다. 하지만 OOP에서는 특정 요소를 추가하거나 삭제 하는 등의 변경사항을 위해 연결된 객체만 수정하면 해당 객체를 사용하는 부분이 추가적인 코드의 삽입과 수정 없이 다 바꿀 수 있다. 이는 코드의 유지보수 적인 측면에서도 시간을 훨씬 단축하여 더 빠른 개발을 가능하게 한다.
객체지향적 설계원칙
앞 글자를 따서 SOLID라고도 불린다.
1.SRP (Single Responsibility Principle) : 단일 책임 원칙. 하나의 클래스는 하나의 책임만 가져야 한다.
여기서는 책임을 기능과 같은 의미로 이해하면 된다. 한 클래스가 책임져야 하는 기능이 여러개라면, 결합도가 올라가고 유지보수 비용이 증가하므로, 책임을 분리시키는게 좋다.
2.OCP (Open-Closed Principle) : 개방-폐쇄 원칙. 기존의 코드를 변경하지 않고, 기능을 수정하거나 추가할 수 있도록 설계한다.
객체를 직접적으로 수정하는 것은 제한해야 하며, 기능이 추가되거나 수정할 때, 객체를 직접적으로 수정해야 한다면 새로운 변경사항에 대해 유연하게 대응할 수 없는 어플리케이션으로 볼 수 있다. 객체를 직접 수정하지 않더라도, 변경사항을 적용할 수 있도록 설계해야 하며, 결과적으로는 객체를 추상화함으로써 확장에는 열려있고, 변경에는 닫혀있는 유연한 구조를 만들 수 있다. 이를 위해서 인터페이스를 사용하게 되면, 확장에는 열려있고(메소드는 있지만 구현은 다양하게 가능), 변경에는 닫혀있는 전략을 구사할 수 있다.
3.LSP (Liskov Substitution Principle) : 리스코프 치환 원칙. 자식클래스는 부모클래스에서 가능한 행위를 수행할 수 있어야 한다.
이는 결국 부모-자식 클래스 사이의 행위에는 일관성이 있어야 한다는 의미이며, 이는 객체지향 프로그래밍에서 부모 클래스의 인스턴스 대신 자식 클래스의 인스턴스를 사용해도 문제가 없어야 한다는 점을 말한다. 결국 상속 관게에서는 일관성이 있는 일반화 관계가 있어야 한다는 말이다.
4.ISP (Interface Segragation Principle) : 인터페이스 분리 원칙. 한 클래스는 자신이 사용하지 않는 인터페이스는 구현하지 말아야 한다.
하나의 일반적인 인터페이스 보다, 여러개의 구체적인 인터페이스가 낫다. 이는 자신이 사용하지 않는 기능에는 영향을 받지 말아야 한다는 의미이다. 예를 들어 스마트폰의 영상통화, 사진촬영, 웹서핑 등의 기능이 있지만 이 기능들은 각각 독립된 인터페이스로 구현하며, 서로에게 영향을 받도록 설계되면 안된다. 이렇게 설계된 소프트웨어의 경우, 내부 의존성을 약화시켜 리팩토링, 유지보수 등이 쉽게 진행될 수 있다.
5.DIP (Dependency Inversion Principle) : 의존 역전 원칙. 의존 관계를 맺을 때, 변화하기 쉬운 것보다 변화하기 어려운 것에 의존해야 한다.
여기서 말하는 변화하기 어려운 것은 추상도가 높은 것을 말하며, 추상클래스나 인터페이스를 의미한다. 따라서 의존 역전 원칙을 만족한다는 것은 의존관계를 맺을 때, 구체적인 클래스보다 인터페이스나 추상 클래스와 관계를 맺는다는 것을 말한다.
객체지향 프로그래밍 키워드
객체지향 프로그래밍을 의미하는 키워드는 총 4개가 있다.
1.캡슐화
비슷한 역할을 하는 속성, 메소드를 하나의 클래스로 모아서 관리. 이것에 추가적으로 캡슐 내부의 로직, 변수를 감추고 외부에는 기능만 제공하는 것을 정보 은닉이라고 한다. 이를 위해서 접근 제어자, getter 및 setter 등의 정의된 오퍼레이션을 통해서만 데이터 접근이 가능하다.
2.추상화
데이터나 프로세스 등을 비슷한 개념이나 의미있는 표현으로 정의하는 과정이다. 하위클래스에 공통적으로 적용된 메소드를 뽑아 인터페이스 처리하는 것도 추상화의 한 예이다. 추상화는 구현 부분을 감춰서 기능의 구현이 아닌 의도를 더 잘 드러낼 수 있게 하며, 유연하게 코드 적용이 가능하다. 하지만 아직 존재하지 않는 기능에 대한 추상화는 복잡도만 증가시킬 수 있기 때문에 실제 변경 및 확장이 일어나는 시점에 추상화를 시도하는게 좋다.
3.다형성
다형성은 말 그대로 여러가지 모습을 갖는 것으로, 객체 지향에서는 한 객체가 여러 타입을 갖는 것을 말한다. 오버라이딩, 오버로딩을 떠올리면 쉽다.
오버로딩은 메소드 명은 같지만 매개변수 숫자, 타입 등을 다르게 해서 재정의하는 것을 말하며, 오버라이딩은 상위 클래스의 메소드를 하위클래스에서 재정의하는 것을 말한다. 다형성은 메소드 하나로 여러가지 역할을 할 수 있게 함으로써 기억하기 쉽고, 메소드의 이름을 절약해준다.
4.상속
간단하게 말해서 부모 클래스의 메소드나 필드를 자식 클래스가 가져다 쓸 수 있게 상속한다. 이에 위에 설명했던 오버라이딩까지 추가하면 결국 기존 클래스에 있던 기능을 추가하거나 재정의 해서 새로운 클래스를 만든다라고 볼 수 있다. 상위 클래스에서는 공통된 메소드를 제공하고, 하위는 상위에 없는 기능을 추가하거나 수정할 수 있게 한다.
어떤 코드가 클린 코드인가?
Good code is code that explains itself.
좋은코드란 가독성이 좋고, 그 자체로 주석등이 필요없이 읽히는 코드이다. 혼자 일하건, 협업을 하건, 어느 시점에 내가 그 코드를 다시 보더라도 이 코드들의 의미가 명학히 이해되는 코드가 되려면, 어떻게 해야할까?
1.검색이 되는 단어
코딩 중간에 추가되는 값을 의미있게 짓고, 만약 변수로 뺄 수 있는 값이라면 변수화 하자.
2.함수명은 동사로
함수는 결국 기능을 위한 것. 정확히 어떤 "기능"을 하는지 묘사하기 위해서는 동사로 사용한다.
또한 함수는 1가지의 기능을 위해서 만들어 놓는 것이기 때문에, 함수 자체가 불필요하게 많은 기능을 담고있는지도 체크할 수 있다.이는 앞서서 설명한 OOP의 SRP와도 연결되는 지점이다.
3.인수는 3개 이하로
함수가 너무 많은 인수를 가지게 되면, 인수의 역할에 대해서도 헷갈릴 수 있다.
4.boolean은 인수로 보내지 말자
boolean을 보내게 되면 결국 안에 조건문으로 분기처리가 되어있다는 의미이므로, 해당 조건문을 별도의 함수로 각각 분리해 주는 편이 좋다.
5.짧은 변수명이나 축약어를 사용하지 않기
당연히 못알아본다. 협업을 위해서 한눈에 파악되는 단어를 쓰는게 좋다.
무엇보다도 클린코드를 처음부터 진행하기는 어렵다. 기능 구현을 한 뒤에 리팩토링을 할 때 클린코드로 다시 코드를 가다듬는 편이 좋다.
레퍼런스
객체지향 5가지 원칙
SOLID 원칙
절차지향 VS 객체지향
깨끗한 코드를 위한 5가지 팁
객체 지향 프로그래밍 입문 - 다형성과 추상화
OOP - 다형성이란?