회원가입

절차 지향 프로그래밍

Beany 2024-09-07

절차 지향 프로그래밍 vs 객체 지향 프로그래밍


프로그래밍 방식은 크게 절차 지향 프로그래밍객체 지향 프로그래밍으로 나눌 수 있다.

 

절차 지향 프로그래밍

  • 절차 지향 프로그래밍은 이름 그대로 절차를 지향한다. 쉽게 이야기해서 실행 순서를 중요하게 생각하는 방식이다.
  • 절차 지향 프로그래밍은 프로그램의 흐름을 순차적으로 따르며 처리하는 방식이다. 즉, "어떻게"를 중심으로 프로그래밍 한다.

객체 지향 프로그래밍

  • 객체 지향 프로그래밍은 이름 그대로 객체를 지향한다. 쉽게 이야기해서 객체를 중요하게 생각하는 방식이다.
  • 객체 지향 프로그래밍은 실제 세계의 사물이나 사건을 객체로 보고, 이러한 객체들 간의 상호작용을 중심으로 프로그래밍하는 방식이다. 즉, "무엇을" 중심으로 프로그래밍 한다.

둘의 중요한 차이

  • 절차 지향은 데이터와 해당 데이터에 대한 처리 방식이 분리되어 있다. 반면 객체 지향에서는 데이터와 그 데이터에 대한 행동(메서드)이 하나의 "객체" 안에 함계 포함되어 있다.

 

절차 지향에서 객체 지향으로 점진적으로 코드를 변경해보면서 객체 지향 프로그래밍을 이해해보자.

 

 

문제: 음악 플레이어 만들기


음악 플레이어를 만들어보자.

 

요구사항:

  1. 음악 플레이어를 켜고 끌 수 있어야 한다.
  2. 음악 플레이어의 볼륨을 증가, 감소할 수 있어야 한다.
  3. 음악 플레이어의 상태를 확인할 수 있어야 한다.

 

예시 출력:

음악 플레이어를 시작합니다
음악 플레이어 볼륨: 1
음악 플레이어 볼륨: 2
음악 플레이어 볼륨: 1
음악 플레이어 상태 확인
음악 플레이어 ON, 볼륨:1
음악 플레이어를 종료합니다

 

 

절차 지향 음악 플레이어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;
}
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("음악 플레이를 종료합니다");
    }
}

음악 플레이어와 관련된 데이터는 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++;
        showVolume(data);
    }

    static void volumeDown(MusicPlayerData data) {
        data.volume--;
        showVolume(data);
    }

    static void showVolume(MusicPlayerData data) {
        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");
        }
    }
}

각각의 기능을 메서드로 만든 덕분에 각각의 기능이 모듈화 되었다. 덕분에 다음과 같은 장점이 생겼다.

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

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

 

절차 지향 프로그래밍의 한계

지금까지 클래스를 사용해서 관련된 데이터를 하나로 묶고, 또 메서드를 사용해서 각각의 기능을 모듈화했다. 덕분에 상당히 깔끔하고 읽기 좋고, 유지보수 하기 좋은 코드를 작성할 수 있었다. 하지만 여기서 더 개선할 수 는 없을까?

우리가 작성한 코드의 한계는 바로 데이터와 기능이 분리되어 있다는 점이다. 음악 플레이어의 데이터는 MusicPlayerData에 있는데, 그 데이터를 사용하는 기능은 MusicPlayerMain3 에 있는 각각의 메서드에 분리되어 있다. 그래서 음악 플레이어와 관련된 데이터는 MusicPlayerData 를 사용해야 하고, 음악 플레이어와 관련된 기능은 MusicPlayerMain3 의 각 메서드를 사용해야 한다.

데이터와 그 데이터를 사용하는 기능은 매우 밀접하게 연관되어 있다. 각각 메서드를 보면 대부분 MusicPlayerData 의 데이터를 사용한다. 따라서 이후에 관련 데이터가 변경되면 MusicPlayerMain3 부분의 메서드들도 함계 변경해야 한다. 그리고 이렇게 데이터와 기능이 분리되어 있으면 유지보수 관점에서도 관리 포인트가 2곳으로 늘어난다.

객체 지향 프로그래밍이 나오기 전까지는 지금과 같이 데이터와 기능이 분리되어 있었다. 따라서 지금과 같은 코드가 최선이었다. 하지만 객체 지향 프로그래밍이 나오면서 데이터와 기능을 온전히 하나로 묶어서 사용할 수 있게 되었다.

즉, MusicPlayerData value 라는 값을 value2 로 수정 되었다가 가정하자. 그러면 MusicPlayerMain3.value 라는 것을 전부 .value2 로 바꿔야 한다.

 

데이터와 기능을 하나로 온전히 묶는다는 것이 어떤 의미인지 이해하기 위해 간단한 예제를 만들어보자.

 

데이터와 기능을 하나로 만들기 전

package oop1;

public class ValueData {
    int value;
}
package oop1;

public class ValueDataMain {
    public static void main(String[] args) {
        ValueData data = new ValueData();
        increase(data);
        increase(data);
        increase(data);
        System.out.println("최종 data.value = " + data.value);
    }

    static void increase(ValueData data) {
        data.value++;
        System.out.println("숫자 증가 data.value = " + data.value);
    }
}
숫자 증가 data.value = 1
숫자 증가 data.value = 2
숫자 증가 data.value = 3
최종 data.value = 3

 

 

데이터와 기능을 하나로 만들기 전

package oop1;

public class ValueData {
    int value;

    void increase() {
        value++;
        System.out.println("숫자 증가 value = " + value);
    }
    
    void get_value() {
        System.out.println("최종 data.value = " + value);
    }
}
package oop1;

public class ValueObjectMain {
    public static void main(String[] args) {
        ValueData data = new ValueData();
        data.increase();
        data.increase();
        data.increase();
        data.get_value();
    }
}

ValueData value 라는 값을 value2 로 수정 했다고 해도 ValueObjectMain 에 영향이 하나도 없다.

0 0
JAVA
이 공간은 개인 공부를 통해 얻은 정보를 체계적으로 정리하고 공유하는 곳입니다. 학습한 내용, 발견한 지식, 그리고 문제 해결 방법 등을 기록하여 나만의 학습 자료를 구축하고, 필요할 때 쉽게 참고할 수 있는 유용한 자원으로 활용할 수 있도록 합니다.
Yesterday: 456
Today: 234