Java

객체지향을 사용하는 이유 (절차지향과 비교)

shininghyunho 2024. 4. 28. 21:11

객체지향이 무엇인가?

객체지향은 데이터를 객체로 만들어 구조화된 프로그래밍 기술입니다.

그렇다면 왜 객체를 굳이 사용해서 프로그래밍 해야할까요?

 

절차지향으로 구현

예를 들어 학생 정보를 관리하는 프로그램을 만든다고 생각해봅시다.

그러면 C 언어를 사용해 구현한다면 다음과 같을것입니다.

#include <stdio.h>

// 구조체 정의
struct Student {
    char name[50];
    int age;
    float score;
};

// 함수 정의
void displayStudent(struct Student s) {
    printf("Name: %s\n", s.name);
    printf("Age: %d\n", s.age);
    printf("Score: %.2f\n", s.score);
}

int main() {
    // 학생 정보 초기화
    struct Student student;
    strcpy(student.name, "John");
    student.age = 20;
    student.score = 3.5;

    // 학생 정보 출력
    displayStudent(student);

    return 0;
}

/*
출력
Name: John
Age: 20
Score: 3.5
*/

 

학생을 구조체로 정의하고

학생의 정보를 출력하는 함수를 만들어 사용하면 됩니다.

 

그런데 학생 정보 뿐만 아니라 교사의 정보도 관리하게된다면 어떻게될까요?

그러면 다음과 같이 교사의 구조체도 추가되고 출력 함수도 추가될것입니다.

 

#include <stdio.h>
#include <string.h>

// 학생 구조체 정의
struct Student {
    char name[50];
    int age;
    float score;
};

// 교사 구조체 정의
struct Teacher {
    char name[50];
    int age;
    char subject[50];
};

// 학생 정보 출력 함수
void displayStudent(struct Student s) {
    printf("Student\n");
    printf("Name: %s\n", s.name);
    printf("Age: %d\n", s.age);
    printf("Score: %.2f\n", s.score);
    printf("\n");
}

// 교사 정보 출력 함수
void displayTeacher(struct Teacher t) {
    printf("Teacher\n");
    printf("Name: %s\n", t.name);
    printf("Age: %d\n", t.age);
    printf("Subject: %s\n", t.subject);
    printf("\n");
}

int main() {
    // 학생 정보 초기화
    struct Student student;
    strcpy(student.name, "John");
    student.age = 20;
    student.score = 3.5;

    // 학생 정보 출력
    displayStudent(student);

    // 교사 정보 초기화
    struct Teacher teacher;
    strcpy(teacher.name, "Mr. Smith");
    teacher.age = 35;
    strcpy(teacher.subject, "Math");

    // 교사 정보 출력
    displayTeacher(teacher);

    return 0;
}

/*
출력
Name: John
Age: 20
Score: 3.5

Name: Mr. Smith
Age: 35
Subject: Math
*/

 

코드가 좀 길어졌습니다.

그렇다면 여기서 그치지않고 다른 교직원의 정보도 같이 관리해야한다면 어떨까요?

다시 구조체와 구조체에 대한 함수가 나올것입니다.

 

그럼 이제 문제점들이 발생하기 시작합니다.

- 구조체 전용으로 쓰이는 함수들(display)이 모두 같은 공간에 정의된다.
구조체 전용 함수들을 모두 사용자가 알맞게 사용해야한다. 구조체마다 전용 함수들을 관리하기 힘들다.
(예를들어 Student에서 쓰이는 함수 10개 Teacher에서 쓰이는 함수 10개가 섞여있다고 생각해보자. 구분이 난감해진다.)

- 구조체별로 중복되는 코드들이 생긴다. (Student와 Teacher에서 공통으로 쓰이는 변수가 존재한다.)

 

왜 이런 문제가 발생하는걸까요?

서로 다른 구조체들끼리 관계를 형성할 수 없기 때문입니다.

 

구조체들끼리 관계를 형성하고 구조체 내의 함수를 사용한다면 문제점들이 해결될것입니다.

이것이 객체입니다.

 

데이터들의 맴버 변수 메서드를 가지면 그것이 객체가 되고,

이러한 객체들끼리 서로 상호작용이 가능해집니다. 

 

객체지향으로 구현

그럼 위의 코드를 객체로 구현해보면 어떻게 될까요?

public class Main {
    // 사람 클래스 정의
    static abstract class Person {
        private String name;
        private int age;

        // 생성자
        public Person(String name, int age) {
            this.name = name;
            this.age = age;
        }

        // 추상 메서드
        public abstract void display();

        // 사람 정보 출력 메서드
        void displayPerson() {
            System.out.println("Name: " + name);
            System.out.println("Age: " + age);
        }
    }

    // 학생 클래스 정의
    static class Student extends Person {
        float score;

        // 생성자
        public Student(String name, int age, float score) {
            super(name, age);
            this.score = score;
        }

        // 재정의된 display 메서드
        @Override
        public void display() {
            displayPerson();
            System.out.println("Score: " + (int)score);
            System.out.println();
        }
    }

    // 교사 클래스 정의
    static class Teacher extends Person {
        private String subject;

        // 생성자
        public Teacher(String name, int age, String subject) {
            super(name, age);
            this.subject = subject;
        }

        // 재정의된 display 메서드
        @Override
        public void display() {
            displayPerson();
            System.out.println("Subject: " + subject);
            System.out.println();
        }
    }

    public static void main(String[] args) {
        // 학생 객체 생성
        Person student = new Student("John", 20, 3.5f);
        // 학생 정보 출력
        student.display();

        // 교사 객체 생성
        Person teacher = new Teacher("Mr. Smith", 35, "Math");
        // 교사 정보 출력
        teacher.display();
    }
}

/*
출력
Name: John
Age: 20
Score: 3

Name: Mr. Smith
Age: 35
Subject: Math
*/

 

한눈에 봐도 C 로 구현했을때보다 main 함수가 굉장히 깔끔해졌습니다.

 

또한 이번엔 둘다 동일한 추상화된 Person 클래스로부터 상속을 받아

Student 와 Teacher 사이에 관계가 생성되었습니다.

이로인해 동일한 display 함수를 재정의하여 사용할 수 있게되었습니다.

 

객체들의 맴버 변수들은 private으로 설정했습니다.

그래서 John의 score 의 구체적인 값은 3.5 지만

외부에 노출되는 display 함수에는 소수점을 없앤 3만 나타나게됩니다.

그래서 중요한 데이터는 숨기고 외부에 노출하고 싶은 데이터만 안전하게 표시할 수 있게되었습니다.

(C로 구현한 예제에서는 student.score로 언제든지 데이터에 접근이 가능합니다.)

 

이것이 객체지향의 특징인 추상화, 상속, 다형성(재정의), 캡슐화(노출 관리) 입니다.

 

결론

 
절차지향으로 간단하게 작성했던 코드들이 복잡해짐에 따라
문제점이 점차 생겨났습니다.
 
그리고 이러한 문제점은 객체지향 설계를 통해 해결할 수 있었습니다.
 
그러면 객체지향의 단점은 없을까요?
 
- 복잡성 : 객체지향 프로그래밍은 추상화, 상속, 다형성, 캡슐화 등의 개념을 사용하여 복잡한 문제를 해결합니다. 이로 인해 구조는 더 안정적일지 몰라도 전체적인 설계는 더욱 복잡해집니다.
- 유지보수성 : 객체지향은 코드의 재사용성과 모듈화가 잘되어 유지보수에 유리합니다. 그러나 반대로 관계가 복잡하다 보니까 유지보수에서 불리한 측면도 있습니다.
- 성능 : 복잡한 객체들간의 구조를 갖게되면 절차지향에 비해 단계가 더욱 늘어납니다.
- 유연성 : 객체지향은 확장에는 열려있어 코드들이 유연해집니다. 그러나 설계가 만약에 잘못되어있다면 복잡한 설계 구조를 다시 만들어야하므로 오히려 유연성이 떨어지게 됩니다.
 
그래서 반드시 객체 지향이 좋지는 않습니다.
객체 지향으로 설계를 하더라도 100% 객체 지향의 모든 단계를 따를 순 없습니다.
그 단계를 모두 따른다면 객체가 너무 많아져 복잡해지기 때문입니다.
 
그래서 프로젝트 규모가 크다면 객체 지향을 지향하되,
단계마다의 상황(성능과 규모)를 고려하여 절차지향을 섞어서 사용해야합니다.

'Java' 카테고리의 다른 글

추상클래스, 인터페이스의 문법적,의미적 차이  (0) 2024.05.22
[객체지향설계원칙] SRP  (0) 2024.05.14
GC는 어떻게 이루어지는가?  (0) 2024.01.29
객체 지향이란?  (0) 2024.01.17
Java의 특징과 동작원리  (0) 2024.01.17