JAVA의 가장 기본이 되는 내용

객체 지향 프로그래밍

cwchoiit 2024. 3. 26. 10:49
728x90
반응형
SMALL

나는 이게 항상 헷갈렸다. 그래서 객체 지향 프로그램은 절차 지향이랑 뭐가 다른데? 비슷한거 같은데 왜 분리 해놓은거야? 

이 두 단어의 의미는 알고 있다. 두 단어의 의미는 다음과 같다.

  • 절차 지향 프로그래밍: 순차적으로 프로그래밍이 실행된다. 작성한 코드의 순서에 중점을 두고 진행된다.
  • 객체 지향 프로그래밍: 프로그램의 동작이나 특정 행위를 객체를 중심으로 코드를 작성하고 그에 맞춰 프로그램이 실행된다.

어떻게 다른걸까? 우선 객체 지향 프로그래밍을 이해하기에 앞서 절차 지향 프로그래밍이 뭔지 알아보자.

 

음악 플레이어 프로그램

다음과 같은 음악 플레이어 프로그램이 있다고 가정해보자.

  • 플레이어를 실행/종료 할 수 있다.
  • 플레이어의 볼륨을 줄이고, 키울 수 있다.
  • 플레이어의 상태를 표시할 수 있다.

그래서 다음과 같이 실행 결과를 출력하는 프로그램이 있다고 생각해보자.

 

그러면 이런식으로 코드를 작성하면 될 것이다. 이 코드는 절차 지향으로 작성된 코드이다.

public class MusicPlayerMain {
    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);

        // 플레이어의 상태 표시
        if (isOn) {
            System.out.println("뮤직 플레이어: ON, 볼륨: " + volume);
        } else {
            System.out.println("뮤직 플레이어: OFF");
        }

        isOn = false;
        System.out.println("뮤직 플레이어를 종료합니다.");
    }
}

 

그런데 이 코드에서 사용되는 변수를 지역변수로 선언하는 게 아니라 클래스로 만들수도 있을 것이다. 그럼 다음과 같은 클래스가 하나 필요하다.

public class Player {
    boolean isOn;
    int volume;
}

 

그리고, 이 클래스의 객체를 만들어서 지역변수를 변경해보자. 이제 지역변수로 데이터를 정의하고 사용하는 게 아니고 클래스의 인스턴스로 데이터 묶음을 가지는 모양이 만들어졌다.

public class MusicPlayerMain {
    public static void main(String[] args) {
        Player player = new Player();

        // 플레이어 실행
        player.isOn = true;
        System.out.println("뮤직 플레이어를 실행합니다.");

        // 플레이어 볼륨 증가
        player.volume++;
        System.out.println("뮤직 플레이어의 볼륨을 증가합니다. 볼륨: " + player.volume);

        // 플레이어 볼륨 증가
        player.volume++;
        System.out.println("뮤직 플레이어의 볼륨을 증가합니다. 볼륨: " + player.volume);

        // 플레이어 볼륨 감소
        player.volume--;
        System.out.println("뮤직 플레이어의 볼륨을 감소합니다. 볼륨: " + player.volume);

        // 플레이어의 상태 표시
        if (player.isOn) {
            System.out.println("뮤직 플레이어: ON, 볼륨: " + player.volume);
        } else {
            System.out.println("뮤직 플레이어: OFF");
        }

        player.isOn = false;
        System.out.println("뮤직 플레이어를 종료합니다.");
    }
}

 

만들고 나니, 중복 코드가 많아보인다. 이 부분을 메서드로 추출해서 중복 코드를 최대한 없애보자. 다음과 같이 정말 깔끔한 코드가 됐다. 

public class MusicPlayerMain {
    public static void main(String[] args) {
        Player player = new Player();

        // 플레이어 실행
        on(player);

        // 플레이어 볼륨 증가
        volumeUp(player);

        // 플레이어 볼륨 증가
        volumeUp(player);

        // 플레이어 볼륨 감소
        volumeDown(player);

        // 플레이어의 상태 표시
        playerStatus(player);

        // 플레이어 종료
        off(player);        
    }

    public static void on(Player player) {
        player.isOn = true;
        System.out.println("뮤직 플레이어를 실행합니다.");
    }

    public static void off(Player player) {
        player.isOn = false;
        System.out.println("뮤직 플레이어를 종료합니다.");
    }

    public static void playerStatus(Player player) {
        if (player.isOn) {
            System.out.println("뮤직 플레이어: ON, 볼륨: " + player.volume);
        } else {
            System.out.println("뮤직 플레이어: OFF");
        }
    }
    
    public static void volumeUp(Player player) {
        player.volume++;
        System.out.println("뮤직 플레이어의 볼륨을 증가합니다. 볼륨: " + player.volume);
    }

    public static void volumeDown(Player player) {
        player.volume--;
        System.out.println("뮤직 플레이어의 볼륨을 감소합니다. 볼륨: " + player.volume);
    }
}

 

그러나, 이것은 정말 깔끔하게 잘 만들어진 절차 지향 프로그래밍이다. 무슨 말이냐면 데이터와 기능이 분리되어 있다. 정작 데이터를 관리하는 곳은 Player라는 클래스인데 그 클래스의 인스턴스(객체)의 데이터를 다루는 기능(플레이어 실행, 플레이어 종료, 볼륨 증가, 볼륨 감소)들은 모두 메인 메서드 안에 정의되어 있다. 이렇게 데이터와 기능이 분리되어 관리되는 방식이 대표적인 절차 지향 프로그래밍이다. 과거에는 모두 이런식으로 프로그램을 만들었다.

 

생각해보면 이상하다 볼륨을 높이고 줄이는 기능은 Player와 아주 아주 밀접하게 연관이 있는데 분리가 되어 있다는 사실이. 그래서 데이터를 관리하는 클래스안에 그 데이터에 대한 특정 행위를 하는 기능을 작성하는 것을 메서드라고 하고 그 메서드와 클래스를 한 곳에 관리하여 프로그램을 작성해보자. 이것이 곧 객체 지향 프로그래밍이다. 객체가 중심이 되는 것이다.

 

그럼 객체가 중심이 되도록 클래스에 데이터와 기능을 모두 같이 관리하도록 위 코드를 리팩토링해보자.

 

객체 지향 프로그래밍

Player

public class Player {
    private boolean isOn;
    private int volume;

    public void on() {
        this.isOn = true;
        System.out.println("뮤직 플레이어를 실행합니다.");
    }

    public void off() {
        this.isOn = false;
        System.out.println("뮤직 플레이어를 종료합니다.");
    }

    public void playerStatus() {
        if (this.isOn) {
            System.out.println("뮤직 플레이어: ON, 볼륨: " + this.volume);
        } else {
            System.out.println("뮤직 플레이어: OFF");
        }
    }

    public void volumeUp() {
        this.volume++;
        System.out.println("뮤직 플레이어의 볼륨을 증가합니다. 볼륨: " + this.volume);
    }

    public void volumeDown() {
        this.volume--;
        System.out.println("뮤직 플레이어의 볼륨을 감소합니다. 볼륨: " + this.volume);
    }
}

Main

public class MusicPlayerMain {
    public static void main(String[] args) {
        Player player = new Player();

        // 플레이어 실행
        player.on();

        // 플레이어 볼륨 증가
        player.volumeUp();

        // 플레이어 볼륨 증가
        player.volumeUp();

        // 플레이어 볼륨 감소
        player.volumeDown();

        // 플레이어의 상태 표시
        player.playerStatus();

        // 플레이어 종료
        player.off();
    }
}

 

위 코드를 보자. 얼마나 깔끔하고 아름다운가? 더 나아가서 이 코드는 아름답기만 할 뿐 아니라, 다른 클래스에서도 객체가 가질 수 있는 모든 기능을 사용할 수 있다. 예전 코드처럼 MusicPlayerMain 클래스에 정의한 기능들은 해당 클래스에서만 사용할 수 있다 (물론 접근제어자 개념과 MusicPlayerMain 클래스의 인스턴스를 만들어서 사용할 순 있지만). 그리고 큰 차이가 있는데, 데이터와 기능을 같은곳에서 관리하기 때문에 전달해줄 파라미터가 없다. 필요가 없다. 본인의 볼륨을 줄이고 키우면 되고 본인을 끄고 키면 되는데 무슨 파라미터가 필요하겠는가? 이렇게 객체가 중심이 되어 객체가 가지는 기능을 그 객체를 만들어내는 클래스에 정의하고 객체를 생성해서 객체가 행할 수 있는 행위들을 실행하는 방식을 객체 지향 프로그래밍이라고 한다.

 

그러니까 객체 지향 프로그래밍은 절차도 중요하지만 절차보다 객체 자체에 더 집중을 하는 방식. 다른 말로 음악 플레이어가 어떤 순서에 의해 어떤 동작을 했느냐보다 어떻게 음악 플레이어를 만들것인가?에 초점을 더 두는것을 객체 지향 프로그래밍이라고 생각하면 될 것 같다.

 

 

그리고 한 가지 더! 캡슐화라는 개념이 여기서 나오는데 플레이어를 구성하기 위한 속성과 기능이 마치 하나의 캡슐에 쌓여있는 것 같다. 이렇게 속성과 기능을 하나로 묶어서 필요한 기능을 메서드를 통해 외부에 제공하는 것을 캡슐화라고 한다. 그리고 이 결과 유지보수와 변경에도 용이해진다. 예를 들어, 플레이어의 'volume'이라는 필드명을 'playerVolume' 이라고 변경해야 할 때 이 플레이어를 가져다 쓰는 외부에 변경이 필요한가? 전혀 필요 없고 해당 클래스에서만 수정하면 된다. 서비스를 실행하는 쪽에서는 어떠한 변경도 필요가 없어진다는 뜻이다. 이게 객체 지향(좀 더 명확하게는 캡슐화)의 장점이라고 생각하면 되겠다.

 

728x90
반응형
LIST