본문 바로가기
IT/김영한의실전JAVA-기본_inFlearn

3. 객체 지향 프로그래밍 - 절차 지향 프로그래밍1(시작) / 절차 지향 프로그래밍2(데이터 묶음) / 절차 지향 프로그래밍3(메서드 추출) / 클래스와 메서드 / 객체 지향 프로그래밍 / 문제와 풀이

by for-learn 2025. 2. 7.

3-1. 절차 지향 프로그래밍1 - 시작

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

 

절차 지향 프로그래밍

  • 절차를 지향하는, 실행 순서를 중요하게 생각하는 방식
  • 프로그램의 흐름을 순차적으로 따르며 처리하는 방식, "어떻게"를 중심으로 프로그래밍
  • 데이터와 해당 데이터에 대한 처리 방식이 분리

객체 지향 프로그래밍

  • 객체를 지향하는, 객체를 중요하게 생각하는 방식
  • 실제 세계의 사물이나 사건을 객체로 보고, 이러한 객체들 간의 상호작용을 중심으로 프로그래밍하는 방식
  • "무엇" 을 중심으로 프로그래밍
  • 데이터와 그 데이터에 대한 행동(메서드)이 하나의 '객체' 안에 함께 포함

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

 

3-2. 절차 지향 프로그래밍2 - 데이터 묶음

앞서 작성된 코드에 클래스를 도입

MusicPlayerData.java (클래스)

package oop1;

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

MusicPlayerMain2.java (절차 지향 음악 플레이어2 - 데이터 묶음)

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 data 객체에 속해 있어 쉽게 구분할 수 있다. 

 

3-3. 절차 지향 프로그래밍3 - 메서드 추출

재사용 될 가능성이 높은 중복된 코드 부분을 메서드를 사용해 기능을 구분

  • 음악 플레이어 켜기, 끄기
  • 볼륨 증가, 감소
  • 음악 플레이어 상태 출력

MusicPlayerMain3.java (절차 지향 음악 플레이어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");
        }
    }
}

각각의 기능을 메서드로 만들어 기능이 모듈화 되었을 때의 장점

  • 중복 제거: 로직 중복이 제거. 같은 로직이 필요하면 해당 메서드를 여러번 호출하면 됨
  • 변경 영향 범위:  기능을 수정할 때 해당 메서드 내부만 변경하면 됨
  • 메서드 이름 추가: 메서드 이름을 통해 코드를 더 쉽게 이해
모듈화: 예로 들어, 레고 블럭처럼 필요한 블럭을 가져다 꼽아서 사용하는 것.
음악 플레이어의 기능이 필요하면 해당 기능을 메서드 호출로 손쉽게 사용 가능. 
음악 플레이어와 관련된 메서드를 조립해 프로그램 작성 가능

 

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

우리가 작성한 코드의 한계는 데이터와 기능이 분리되어 있다는 점.

  • 음악 플레이어의 데이터는 MusicPlayerData에 있는데,
    그 데이터를 사용하는 기능은 MusicPlayerMain3에 있는 각각의 메서드에 분리되어 있음.
  • 데이터와 그 데이터를 사용하는 기능은 매우 밀접하게 연관되어 있어,
     관련 데이터가 변경되면 MusicPlayerMain3 부분의 메서드들도 함께 변경해야 함.
  • 데이터와 기능이 분리되어 있으면 유지보수 관점에서도 관리 포인트가 2곳으로 늘어남

객체 지향 프로그래밍이 나오면서 데이터와 기능을 온전히 하나로 묶어서 사용 가능해 짐

 

3-4. 클래스와 메서드

클래스는 속성(데이터, 멤버 변수)기능(메서드)를 정의함

ValueData.java (멤버 변수만 존재하는 클래스)

package oop1;

public class ValueData {
    int value;
}

ValueDataMain.java 

package oop1;

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

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

ValueObject.java (속성과 기능을 함께 포함하는 클래스)

package oop1;

public class ValueObject {
    int value;
    
    void add() {
        value++;
        System.out.println("숫자 증가 value="+value);
    }
}
  • 클래스 안의 add() 메서드에는 static 키워드를 사용하지 않음
  • 메서드는 객체를 생성해야 호출할 수 있는데, static이 붙으면 객체를 생성하지 않고 메서드 호출이 가능

ValueObjectMain.java

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);
    }
}
  • 객체는 자신의 메서드를 통해 자신의 멤버 변수에 접근 가능

인스턴스 생성과, 인스턴스의 메서드 호출

 

3-5. 객체 지향 프로그래밍

  • 프로그램의 실행 순서보다는 음악 플레이어라는 개념을 객체로 온전히 만드는 것을 지향
  • 음악 플레이어 클래스를 만드는 것 자체에 집중
  • 음악 플레이어 가 어떤 속성(데이터)을 가지고 어떤 기능(메서드)을 제공하는지 이 부분에 초점
    • 속성: volume, isOn
    • 기능: on(), off(), volumeUp(), volumeDown(), showStatus()
  • 음악 플레이어를 만들어서 제공하는 개발자와 음악 플레이어를 사용하는 개발자를 분리해서 생각
  • 음악 플레이어를 개발하는 개발자가 될 것

MusicPlayer.java (객체 지향 음악 플레이어)

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");
        }
    }
}

ValuePlayerMain4.java

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 => 음악 플레이어를 구성하기 위한 속성과 기능이 마치 하나의 캡슐에 쌓여있는 것 같다

 

3-6. 문제와 풀이

RectangleProceduralMain.java (절차 지향 코드)

package oop1.ex;

public class RectangleProceduralMain {
    public static void main(String[] args) {
        int width = 5;
        int height = 8;
        int area = calculateArea(width, height);
        System.out.println("넓이: " + area);
        
        int perimeter = calculatePerimeter(width, height);
        System.out.println("둘레 길이: " + perimeter);
        
        boolean square = isSquare(width, height);
        System.out.println("정사각형 여부: " + square);
    }
    
    static int calculateArea(int width, int height) {
        return width * height;
    }
    static int calculatePerimeter(int width, int height) {
        return 2 * (width + height);
    }
    static boolean isSquare(int width, int height) {
        return width == height;
    }
}

Rectangle.java (객체 지향으로 변경)

package oop1.ex;

public class Rectangle {
    int width;
    int height;

    int calculateArea() {
        return width * height;
    }

    int calculatePerimeter() { // 둘레 계산
        return 2 * (width + height);
    }

    boolean isSquare() {
        return width == height;
    }
}

RectangleOopMain.java

package oop1.ex;

public class RectangleOopMain {
    public static void main(String[] args) {
        Rectangle rectangle = new Rectangle();
        rectangle.width = 5;
        rectangle.height = 8;

        int area = rectangle.calculateArea();
        System.out.println("넓이: " + area);

        int perimeter = rectangle.calculatePerimeter();
        System.out.println("둘레 길이: " + perimeter);

        boolean square = rectangle.isSquare();
        System.out.println("정사각형 여부: " + square);
    }
}

 

Account.java

package oop1.ex;

public class Account {
    int balance;

    void deposit(int amount) {
        balance += amount;
    }

    void withdraw(int amount) {
        if (amount <= balance) {
            balance -= amount;
        } else {
            System.out.println("잔액 부족");
        }
    }
}

AccountMain.java

package oop1.ex;

public class AccountMain {
    public static void main(String[] args) {
        Account account = new Account();

        account.deposit(10000);
        account.withdraw(9000);
        account.withdraw(2000);
        System.out.println("잔고: " + account.balance);
    }
}

 

객체란?

세상의 모든 사물을 단순하게 추상화 해보기

  • 자동차
    • 속성: 차량 색상, 현재 속도
    • 기능: 엑셀, 브레이크, 문 열기, 문 닫기
  • 동물
    • 속성: 색상, 키, 온도, 나이
    • 기능: 먹는다, 걷는다, 잔다