처음부터 차근차근

[Java] 객체 지향 프로그래밍 본문

Language/Java

[Java] 객체 지향 프로그래밍

HangJu_95 2023. 12. 24. 20:54
728x90

Java는 Class 기반 객체 지향 프로그래밍입니다.

하지만 지금까지 작성했던 코드들은 모두 절차 지향 프로그래밍이였습니다.

두 개의 차이점과 어떻게 하면 객체 지향 프로그래밍으로 작성하는지 알아보겠습니다.

절차 지향 vs 객체 지향

  • 절차 지향 프로그래밍
    • 절차를 지향합니다. 쉽게 이야기해서 실행 순서를 중요하게 생각하는 방식입니다.
    • 프로그램의 흐림이 순차적으로 따르며 처리하는 방식입니다. 즉, "어떻게"를 중심으로 프로그래밍 합니다.
  • 객체 지향 프로그래밍
    • 이름 그대로 객체를 지향합니다. 쉽게 이야기해서 객체를 중요하게 생각하는 방식입니다.
    • 실제 세계의 사물이나 사건을 객체로 보고, 이러한 객체들 간의 상호작용을 중심으로 프로그래밍 하는 방식입니다. 즉, "무엇을" 중심으로 프로그래밍 합니다.
  • 차이점
    • 절차 지향은 데이터와 해당 데이터에 대한 처리 방식이 분리되어 있습니다.
    • 객체 지향에서는 데이터와 그 데이터에 대한 행동(메서드)이 하나의 "객체"안에 함께 포함되어 있습니다.

예시 : 음악 플레이어 만들기

음악 플레이어 (MP3) 프로그래밍을 간단하게 만들어보겠습니다.

  • 요구 사항
    • 음악 플레이어를 켜고 끌 수 있어야 한다.
    • 음악 플레이어의 볼륨을 증가, 감소할 수 있어야 한다.
    • 음악 플레이어의 상태를 확인할 수 있어야 한다.

1. 절차 지향 음악 플레이어

순서대로 프로그램이 작동하도록 절차 지향적으로 만들었습니다.

package oop1;

public class MusicPlayerMain1 {
    public static void main(String[] args) {
        int volume = 0;
        boolean isOn = false;
        //음악 플레이어 켜기
        isOn = true;
        System.out.println("음악 플레이어를 시작합니다");

        //볼륨 증가
        volume++;
        System.out.println("음악 플레이어 볼륨:" + volume);

        //볼륨 증가
        volume++;
        System.out.println("음악 플레이어 볼륨:" + volume);

        //볼륨 감소
        volume--;
        System.out.println("음악 플레이어 볼륨:" + volume);

        //음악 플레이어 상태
        System.out.println("음악 플레이어 상태 확인");
        if (isOn) {
            System.out.println("음악 플레이어 ON, 볼륨:" + volume);
        } else {
            System.out.println("음악 플레이어 OFF");
        }

        //음악 플레이어 끄기
        isOn = false;
        System.out.println("음악 플레이어를 종료합니다");
    }
}

2. 데이터 묶음

앞서 작성한 코드에 클래스를 도입하여 음악 플레이어에 사용되는 데이터들을 묶어서 멤버 변수로 사용해보겠습니다.

package oop1;

public class MusicPlayerData {
    int volume = 0;
    boolean isOn = false;
}

음악 플레이어에 사용되는 volume, isOn 속성을 MusicPlayerData의 멤버 변수에 포함하여 관리하겠습니다.

package oop1;

/**
 * 음악 플레이어와 관련된 데이터 묶기
 */
public class MusicPlayerMain2 {
    public static void main(String[] args) {
        MusicPlayerData data = new MusicPlayerData();
//음악 플레이어 켜기
        data.isOn = true;
        System.out.println("음악 플레이어를 시작합니다");
//볼륨 증가
        data.volume++;
        System.out.println("음악 플레이어 볼륨:" + data.volume);
//볼륨 증가
        data.volume++;
        System.out.println("음악 플레이어 볼륨:" + data.volume);
//볼륨 감소
        data.volume--;
        System.out.println("음악 플레이어 볼륨:" + data.volume);
//음악 플레이어 상태
        System.out.println("음악 플레이어 상태 확인");
        if (data.isOn) {
            System.out.println("음악 플레이어 ON, 볼륨:" + data.volume);
        } else {
            System.out.println("음악 플레이어 OFF");
        }
//음악 플레이어 끄기
        data.isOn = false;
        System.out.println("음악 플레이어를 종료합니다");
    }
}

MP3와 관련된 데이터는 MusicPlayerData 클래스에 존재하며, 이후 프로그램 로직이 더 복잡해져서 다양한 변수들이 추가되더라도 음악 플레이어와 관련된 변수들은 MusicPlayerData data 객체에 속해있으므로 쉽게 구분할 수 있습니다.

3. 반복되는 메서드 추출

중복되는 코드를 사용하고, 이후에 사용될만한 기능들을 한번 메서드 형태로 만들어보겠습니다.

package oop1;

/**
 * 메서드 추출
 */
public class MusicPlayerMain3 {
    public static void main(String[] args) {
        MusicPlayerData data = new MusicPlayerData();
        //음악 플레이어 켜기
        on(data);
        //볼륨 증가
        volumeUp(data);
        //볼륨 증가
        volumeUp(data);
        //볼륨 감소
        volumeDown(data);
        //음악 플레이어 상태
        showStatus(data);
        //음악 플레이어 끄기
        off(data);
    }
    static void on(MusicPlayerData data) {
        data.isOn = true;
        System.out.println("음악 플레이어를 시작합니다");
    }
    static void off(MusicPlayerData data) {
        data.isOn = false;
        System.out.println("음악 플레이어를 종료합니다");
    }
    static void volumeUp(MusicPlayerData data) {
        data.volume++;
        System.out.println("음악 플레이어 볼륨:" + data.volume);
    }
    static void volumeDown(MusicPlayerData data) {
        data.volume--;
        System.out.println("음악 플레이어 볼륨:" + data.volume);
    }
    static void showStatus(MusicPlayerData data) {
        System.out.println("음악 플레이어 상태 확인");
        if (data.isOn) {
            System.out.println("음악 플레이어 ON, 볼륨:" + data.volume);
        } else {
            System.out.println("음악 플레이어 OFF");
        }
    }
}

각각의 기능을 메서드로 만든 덕분에 각각의 기능이 모듈화 되었습니다.

모듈화: 쉽게 이야기해서 레고 블럭을 생각하면 된다. 필요한 블럭을 가져다 꼽아서 사용할 수 있다. 여기서는 음악 플레이어의 기능이 필요하면 해당 기능을 메서드 호출 만으로 손쉽게 사용할 수 있다. 이제 음악 플레이어와 관련된 메서드를 조립해서 프로그램을 작성할 수 있다.

모듈화 덕분에 다음과 같은 장점이 생겼습니다.

  • 중복 제거: 로직 중복이 제거되었다. 같은 로직이 필요하면 해당 메서드를 여러번 호출하면 된다.
  • 변경 영향 범위: 기능을 수정할 때 해당 메서드 내부만 변경하면 된다.
  • 메서드 이름 추가: 메서드 이름을 통해 코드를 더 쉽게 이해할 수 있다.

그러나 아직까지 한계점이 존재합니다.

  • 데이터데이터를 사용하는 기능이 분리되어 있다는 점
  • 관련 데이터가 변경되면 메서드들도 함께 변경이 되어야 한다.
  • 유지보수 관점에서도 관리 Point가 2곳으로 늘어난다는 단점

데이터와 데이터를 사용하는 기능을 한번에 묶을 순 없을까? 객체 지향 프로그래밍에서는 가능합니다.

클래스와 메서드

Class는 데이터인 멤버 변수 뿐 아니라 기능 역할을 하는 메서드도 포함할 수 있습니다.

package oop1;

public class ValueObject {
    int value;

    void add() {
        value++;
        System.out.println("숫자 증가 value=" + value);
    }
}

ValueObject Class는 value인 데이터와 해당 데이터를 사용하는 기능인 add() 메서드를 함께 정의하였습니다.

참고: 여기서 만드는 add() 메서드에는 static 키워드를 사용하지 않는다.
메서드는 객체를 생성해야 호출할 수 있다. 그런데 `static` 이 붙으면 객체를 생성하지 않고도 메서드를 호출할 수 있다.
static에 대한 자세한 내용은 뒤에서 설명한다.
package oop1;

public class ValueObjectMain {
    public static void main(String[] args) {
        ValueObject valueObject = new ValueObject();
        valueObject.add();
        valueObject.add();
        valueObject.add();
        System.out.println("최종 숫자=" + valueObject.value);
    }
}

어떻게 동작하는지 한번 알아보겠습니다.

 

인스턴스 생성

ValueObject valueObject = new ValueObject();

ValueObject 인스턴스 생성

ValueObject라는 인스턴스를 하나 생성했습니다. 이 객체에는 멤버 변수 뿐만 아니라 내부에 기능을 수행하는 add() 메서드도 함께 존재합니다.

 

인스턴스의 메서드 호출

인스턴스의 메서드 호출

인스턴스의 메서드를 호출하는 방법은 멤버 변수를 사용하는 방법과 동일합니다.

(Javascript도 마찬가지로 사용하니..)

객체에 접근한 다음, .(dot)을 찍어서 원하는 메서드를 호출합니다.

valueObject.add(); //1
x002.add(); //2: x002 ValueObject 인스턴스에 있는 add() 메서드를 호출한다.

3: add() 메서드를 호출하면 메서드 내부에서 value++을 호출하게 된다. 이때 value에 접근해야 하는데, 기본으로 본인 인스턴스에 있는 멤버 변수에 접근한다. 본인 인스턴스가 x002참조값을 사용하므로 자기 자신인 x002.value 에 접근하게 된다.

4: ++ 연산으로 value의 값을 하나 증가시킨다.

 

간단히 정리하자면

  • 클래스는 속성(데이터, 멤버 변수)과 기능(메서드)을 정의할 수 있다.
  • 객체는 자신의 메서드를 통해 자신의 멤버 변수에 접근할 수 있다.
    • 객체의 메서드 내부에서 접근하는 멤버 변수는 객체 자신의 멤버 변수이다.

객체 지향 프로그래밍

이제 데이터와 기능을 하나로 묶어서 음악 플레이어라는 개념을 온전히 하나의 클래스에 담아보겠습니다.

그러기 위해서는 프로그램의 실행 순서보다는 음악 플레이어 클래스를 만드는 것 자체에 집중해야 합니다. 음악 플레이어가 어떤 속성(데이터)을 가지고 어떤 기능(메서드)을 제공하는지 이 부분에 초점을 맞추어야 합니다.

  • 속성 : volume, isOn
  • 기능 : on(), off(), volumeUp(), volumeDown(), showStatus()
package oop1;

public class MusicPlayer {
    int volume = 0;
    boolean isOn = false;

    void on() {
        isOn = true;
        System.out.println("음악 플레이어를 시작합니다.");
    }
    void off() {
        isOn = false;
        System.out.println("음악 플레이어를 종료합니다");
    }
    void volumeUp() {
        volume++;
        System.out.println("음악 플레이어 볼륨:" + volume);
    }
    void volumeDown() {
        volume--;
        System.out.println("음악 플레이어 볼륨:" + volume);
    }
    void showStatus() {
        System.out.println("음악 플레이어 상태 확인");
        if (isOn) {
            System.out.println("음악 플레이어 ON, 볼륨:" + volume);
        } else {
            System.out.println("음악 플레이어 OFF");
        }
    }
}

 

package oop1;

/**
 * 객체 지향 프로그래밍 방식
 */
public class MusicPlayerMain4 {
    public static void main(String[] args) {
        MusicPlayer player = new MusicPlayer();
        //음악 플레이어 켜기
        player.on();
        //볼륨 증가
        player.volumeUp();
        //볼륨 증가
        player.volumeUp();
        //볼륨 감소
        player.volumeDown();
        //음악 플레이어 상태
        player.showStatus();
        //음악 플레이어 끄기
        player.off();
    }
}

MusicPlayer를 사용하는 코드를 보면, MusicPlayer 객체를 생성하고 필요한 기능(메서드)을 호출하기만 하면 됩니다.

  • MusicPlayer를 사용하는 입장에서는 MusicPlayer의 데이터인 volume, isOn 같은 데이터를 전혀 사용하지 않습니다.
  • 또한 내부에 어떤 속성이 있는지 전혀 몰라도 되며, 단순하게 MusicPlayer가 제공하는 기능 중에 필요한 기능을 호출해서 사용하기만 하면 됩니다.

캡슐화

이런식으로 속성과 기능을 하나로 묶어서 필요한 기능을 메서드를 통해 외부에 제공하는 것을 캡슐화라 합니다.

 

객체 지향 프로그래밍 덕분에 음악 플레이어 객체를 사용하는 입장에서 진짜 음악 플레이어를 만들고 사용하는 것 처럼 친숙하게 느껴집니다. 그래서 코드가 더 읽기 쉬운 것은 물론이며, 속성과 기능이 한 곳에 있기 때문에 변경도 더 쉬워집니다.

객체 지향 프로그래밍은 모든 사물을 속성과 기능을 가진 객체로 생각하는 것이다. 객체에는 속성과 기능만 존재한다.
이렇게 단순화하면 세상에 있는 객체들을 컴퓨터 프로그램으로 쉽게 설계할 수 있다.
이런 장점들 덕분에 지금은 객체 지향 프로그래밍이 가장 많이 사용된다.
참고로 실세계와 객체가 항상 1:1로 매칭되는 것은 아니다.

참고

 

김영한의 실전 자바 - 기본편 강의 - 인프런

실무에 필요한 자바 객체 지향의 핵심 개념을 예제 코드를 통해 쉽게 학습합니다., 국내 개발 분야 누적 수강생 1위, 제대로 만든 김영한의 실전 자바[사진][임베딩 영상]단순히 자바 문법을 안다

www.inflearn.com

 

'Language > Java' 카테고리의 다른 글

[Java] Package  (1) 2023.12.26
[Java] 생성자, this  (1) 2023.12.26
[Java] 변수와 초기화, null, null  (0) 2023.12.21
[Java] 기본형과 참조형  (1) 2023.12.21
[Java] Class와 데이터  (1) 2023.12.21