업데이트:

카테고리: ,

객체지향 프로그래밍


등장 배경

옛날 개발자들은 순차적, 비구조적 프로그래밍을 사용했다. 말 그대로 순차적으로 하나씩 코딩해나가는 것이다. 그러나 이런 방식은 코드가 길어지고 복잡해진다. 따라서 클린코드가 아니게 되어 다른 사람이 보면 해석하기가 참 힘들어진다.

이런 문제를 해결하기 위해 절차적, 구조적 프로그래밍이 나타났다. 이것은 반복될 가능성이 있는 것들을 재사용이 가능한 함수(프로시저)로 만들어 사용하는 프로그래밍 방식이다.

여기서 보통 절차라는 의미는 함수(프로시저)를 뜻하고, 구조는 모듈/함수를 뜻한다.

프로시져란?

반환값(리턴)이 따로 존재하지 않는 함수를 뜻한다. 예를 들면, printf와 같은 함수는 반환값을 얻기 위한 것보단, 화면에 출력하는 용도로 쓰이는 함수다. 이와 같은 함수를 프로시저로 부른다.

그러나 절차적, 구조적 프로그래밍은 자료형 데이터와 함수들을 따로 따로 물리적으로 설정하게 된다. 그래서 만약 많은 데이터를 생성해야할 경우엔 비효율적으로 코딩할 가능성이 높아진다.

따라서, 이를 한번에 묶기 위한 패러다임이 탄생하는데, 이것이 바로 객체지향 프로그래밍이다.

객체지향 프로그래밍이란?

객체지향 프로그래밍이란, 객체 내부에 자료형(필드)와 함수(메소드)가 같이 존재하는 것이다. 객체지향으로 구현하게 되면, 객체 간의 독립성이 생기고 중복코드의 양이 줄어드는 장점이 있다. 또한 독립성이 확립되면 유지보수에도 도움이 될 것이다.

특징

객체지향의 패러다임이 생겨나면서 크게 4가지 특징을 갖추게 되었다. 이 4가지 특성을 잘 이해하고 구현해야 객체를 통한 효율적인 구현이 가능해진다.

추상화(Abstraction)

추상화란, 세부적인 사물들의 공통적인 특징을 파악한 후, 필요로 하는 속성이나 행동을 추출하는 작업이다.

캡슐화(Encapsulation)

캡슐화란, 한 곳에서 변화가 일어나도 다른 곳에 미치는 영향을 최소화 시킬 수 있게 낮은 결합도를 유지하는 것을 말한다.

결합도가 낮도록 만들어야 하는 이유가 무엇일까? 결합도(coupling)란, 어떤 기능을 실행할 때 다른 클래스나 모듈에 얼마나 의존적인가를 나타내는 말이다.

즉, 독립적으로 만들어진 객체들 간의 의존도가 최대한 낮게 만드는 것이 중요하다. 객체들 간의 의존도가 높아지면 굳이 객체 지향으로 설계하는 의미가 없어진다. 그러나, 객체 안의 모듈 간의 요소가 밀접한 관련이 있는 것으로 구성하여 응집도를 높이고 결합도를 줄여야 요구사항 변경에 대처하는 좋은 설계 방법이라고 한다.

캡슐화의 높은 응집도와 낮은 결합도는 정보 은닉을 활용하면 된다. 외부에서 접근할 필요가 없는 것들은 private으로 접근하지 못하도록 제한을 둔다고 한다. 파이썬의 경우의 private을 사용하는 예시를 봐보자.


class Spam:
    def __init__(self):
        self.__attr = 999
    def method(self):
        self.__method()
    def __method(self):
        print(self.__attr)
    def run(self):
        self.__method()

spam = Spam()
spam.run()
spam.__method()

위 코드를 실행한다면, 에러가 나게 된다. 왜냐하면 클래스 객체의 __method()는 private 메서드이기 때문이다. 그래서 외부에서 접근을 못한다.

제일 마지막 줄을 지운다면, run() 메서드가 실행되고, __method()가 내부적으로 실행되며 999가 출력된다. 즉, 내부적인 응집도는 상승하고, 외부적인 결집력은 감소하게 되는 것이다.

상속

일반화(상속)은 또 다른 캡슐화다. 자식 클래스를 외부로부터 은닉하는 캡슐화의 일종이라고 말할 수 있다. 음.. 자세한 설명은 좀 이해가 안간다. 일단 보류.

다형성(Polymorphism)

객체 지향의 핵심과도 같은 부분이다.

다형성은, 상속과 함께 활용할 때 큰 힘을 발휘한다. 이와 같은 구현은 코드를 간결하게 해주고, 유연함을 갖추게 해준다.

즉, 부모 클래스의 메소드를 자식 클래스가 오버라이딩해서 자신의 역할에 맞게 활용하는 것이 다형성이다.

오버라이딩이란? 부모 클래스로부터 상속받은 메서드의 내용을 변경하는 것을 오버라이딩이라 한다.

파이썬의 예시를 보자.


class Person:
    def greeting(self):
        print('안녕하세요.')
 
class Student(Person):
    def greeting(self):
        print('안녕하세요. 저는 파이썬 코딩 도장 학생입니다.')
 
james = Student()
james.greeting()

위 결과는 안녕하세요. 가 아닌 안녕하세요. 저는 파이썬 코딩 도장 학생입니다.가 나오게 된다. 자식 클래스 Student는 부모 클래스 Person을 상속받아서 그 안의 greeting 메서드를 자신만의 메서드로 오버라이딩했기 때문이다.

이처럼 다형성을 사용하면, 구체적으로 현재 어떤 클래스 객체가 참조되는 지는 무관하게 프로그래밍하는 것이 가능하다. 상속 관계에 있으면, 새로운 자식 클래스가 추가되어도 부모 클래스의 함수를 참조해오면 되기 때문에 다른 클래스는 영향을 받지 않게 된다. 이 말은 즉, 새로 자식 클래스를 생성하더라도 이전에 오버라이딩 된 것은 새로운 클래스와 연관이 없다는 말이다. 예시를 보자.


class Person:
  def greeting(self):
    print('안녕하세요.')


class Student(Person):
  def greeting(self):
    print('안녕하세요. 저는 파이썬 코딩 도장 학생입니다.')


class Student2(Person):
  def greeting2(self):
    print('안녕하세요. 나는 무관해요.')


james = Student()
james.greeting() 
james2 = Student2()
james2.greeting2()

결과는 돌려보지 않아도 알 것이다.

객체 지향 설계 원칙

SOLID라고 부르는 5가지 설계 원칙이 존재한다.

음.. 당장 막 중요하진 않을 것 같다. 나중에 보고싶으면 링크를 가서 보자.



Reference


2 분 소요