우당탕탕 개발_𝒍𝒐𝒈

객체 지향 프로그래밍 본문

𝐬𝐭𝐮𝐝𝐲/𝐉𝐚𝐯𝐚

객체 지향 프로그래밍

hojeong01 2024. 2. 4. 17:39
절차 지향 코드를 객체 지향 코드로 리펙토링 하며 객체 지향 프로그래밍에 대한 개념을 이해한다. 

 


프로그래밍 방식

  • 절차 지향 프로그래밍 (모듈화) <그림 추가>
  • 객체 지향 프로그래밍 (캡슐화) <그림 추가>
절차 지향 프로그래밍 . . .               객체 지향 프로그래밍
"How"를 중심으로, 순차적으로 코드의 흐름에 따라 처리하는 프로그래밍 방식 방식 실제 세계의 사물, 사건을 의미하는 객체 간의 상호작용을 중심으로 둔 프로그래밍 방식
분리 차이점
(기준 : 데이터와 기능에 대한 처리 방식)
'객체'안에 포함

Music Player 만들기


#1 요구사항

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

#2 절차지향 프로그래밍의 문제점

- 요구사항에 따라  절차지향적으로 아래와 같이 코드를 작성해 본 후 문제점을 탐색해 보았습니다. 

//음악 플레이어의 볼륨, 상태 초기값 지정
int volume =0;
boolean isOn = true;

//음악플레이어 키기
OnAndOff = true;
System.out.println("플레이어를 실행시킵니다.");

//볼륨 증가
volume ++;
System.out.println("볼륨 증가:"+ volume);
//볼륨 증가
volume ++;
System.out.println("볼륨 증가:"+ volume);
//볼륨 감소
volume --;
System.out.println("볼륨 감소:"+ volume);


//상태 확인
if (isOn){
    System.out.println("플레이어가 실행되고 있습니다.");
}else {
    System.out.println("플레이어가 종료되었습니다.");
}

//종료
isOn = false;
System.out.println("플레이어를 종료합니다.");
  • 플레이어의 속성과 기능이 명확하게 분리가 되어있지 않아 가독성이 떨어질 수 있습니다.
  • 볼륨 증가 및 감소에 대한 기능의 코드가 중복되어  있습니다. 이는 코드의 유지/보수를 어렵게 합니다. 
  • 속성을 변경하는 부분이 여러 곳에 배치되어 있어 추적 및 디버깅에 어려움을 줄 수 있습니다.

#3 절차지향 ➡️객체지향

순서 : 데이터 묶기 >> 메서드 추출 >> 클래스 안에 메서드 넣기

 


1. 데이터 묶기 

- MusicPlayerData  플레이어의 속성을 가진 클래스 생성 

- main  메서드에 new 예약어를 사용하여 MusicPlayerData의 참조값을 가진 data 인스턴스 생성

-.(dot)을 통해 해당 데이터를 가져옴 

public class MusicPalyerData {

    //음악 플레이어 속성
    int volume = 0;
    boolean isOn = true;
}
public static void main(String[] args) {

    MusicPalyerData data= new MusicPalyerData();
    //음악 플레이어의 볼륨, 상태 초기값 지정
   /* int volume =0;
    boolean isOn = true;*/

    //음악플레이어 키기
    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);


    //상태 확인
    if ( data.isOn){
        System.out.println("플레이어가 실행되고 있습니다.");
    }else {
        System.out.println("플레이어가 종료되었습니다.");
    }

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

  • 위 과정을 통해 조금이나마 데이터의 관리가 용이해졌습니다. 하지만 아직까지도 중복코드가  많이 보이기 때문에 메서드를 통해 제거해 보겠습니다.

2. 메서드 추출하기 

 

⭐ 재사용될 가능성이 높은 기능을 메서드 속에 담아보자!

- 플레이어 on, off / volume up, down / 플레이어 상태 (showpalyer)

public static void main(String[] args) {

    MusicPalyerData data= new MusicPalyerData();


    //음악플레이어 켜기
    on(data);
    //볼륨 증가
    volumeUp(data);
    //볼륨 증가
    volumeUp(data);
    //볼륨 감소
    volumeDown(data);
    //종료
    off(data);
    //상태 확인
    showPlayer(data);


}
static void on(MusicPalyerData data){
    data.isOn = true;
    System.out.println("플레이어를 실행시킵니다.");
}
static void off(MusicPalyerData data){
    data.isOn = false;
    System.out.println("음악 플레이어를 종료합니다.");
}
static void volumeUp(MusicPalyerData data){
    data.volume ++;
    System.out.println("볼륨:"+  data.volume);
}
static void volumeDown(MusicPalyerData data){
    data.volume --;
    System.out.println("볼륨:"+  data.volume);
}
static void showPlayer(MusicPalyerData data){
    if ( data.isOn){
        System.out.println("플레이어가 실행되고 있습니다.");
    }else {
        System.out.println("플레이어가 종료되었습니다.");
    }
}

메서드를 추출하여 리펙토링하는 과정에서 다음과 같은 메서드의 장점을 발견할 수 있었습니다. 

  1. 중복 코드 개선 : 중복되는 코드를 줄였습니다. 
  2. 변경 사항 범위 : 변경 사항이 생겼을 때 메서드 내부의 기능만 수정해도 되어 유지 보수 면에서 개선되었습니다. 
  3. 메서드 이름 : 메서드의 이름을 사용하여 기능의 역할을 직관적으로 알 수 있게 되었습니다. 

하지만 아직까지 절차 지향적으로 작성되어 있는 것을 확인할 수 있습니다(기능, 데이터 구분되어 있음)

이는 유지 보수를 할 곳이 2곳이 되었다는 것으로 보다 효율적인 유지보수가 어려울 수 있습니다. 

따라서 이후 단계에서는 기능과 데이터를 한 클래스 속에 담아 위 예시 코드보다 객체지향적인 코드가 될 수 있도록 리펙토링 해보겠습니다. 

 


 3. 클래스 

 

⭐ 클래스 속에는 멤버변수(속성), 메서드(기능)를 모두 포함할 수 있습니다.

 

-MusicPlayer 클래스 생성 후 플레이어의 속성과 기능을 담아 두었습니다. 

public class MusicPlayer {

    //속성
    int volume = 0;
    boolean isOn =true;

    //기능
    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 showPlayer(){
        if (isOn){
            System.out.println("플레이어가 실행되고 있습니다.");
        }else {
            System.out.println("플레이어가 종료되었습니다.");
        }
    }
}

 

-main 클래스에서 객체를 생성, 클래스를 호출하였습니다. 

public static void main(String[] args) {
    MusicPlayer musicPlayer = new MusicPlayer();

    musicPlayer.on();
    musicPlayer.volumeUp();
    musicPlayer.volumeUp();
    musicPlayer.volumeDown();
    musicPlayer.showPlayer();
    musicPlayer.off();
}

 

메모리 그림 추가

<  >

 

⭐플레이어의 기능과 상태를 하나의 클래스 속에 담게 되면서 

수정사항이 생길 시 해당 클래스 내부에서만 수정하면 되기 때문에 절차지향적인 코드보다 유지-보수가 더욱 간편해진다는 장점을 확인할 수 있습니다.


 

회고

지금까지 '단순히 객체를 생성하고, 메서드를 분리하여 코드를 작성하는 것이 객체지향적인 코드이다'라고 착각하며 코딩을 했다는 것을 깨달았습니다.
코드를 리펙토링 하는 과정 속에서MusicPlayer 클래스 속 메서드는 '왜 리턴 타입만을 가지는가'에 대한 궁금증이 생겼습니다. 이에 관련하여 다음에는 static에 대해 알아보려 합니다.

'𝐬𝐭𝐮𝐝𝐲 > 𝐉𝐚𝐯𝐚' 카테고리의 다른 글

접근 제어자  (0) 2024.02.10