본 글의 저작권과 원문은 https://blog.naver.com/sweetie_rex 에 있습니다.
현재의 글은 업데이트가 되지 않음을 유의해서 읽어주세요.
최신 업데이트 된 글을 읽으시려면 아래의 링크를 확인해주세요.

객체 지향 프로그래밍 (OOP, Object-Oriented Programming)

이제부터 본격적으로 객체 지향 프로그래밍 (OOP, Object-Oriented Programming) 이라는 것이 무엇인지 설명하려고 해.

  • Object(객체)
  • Oriented(지향)
  • Programming(프로그래밍)

위 3개의 단어로 설명을 해보자면, "프로그래밍 로직을 작성하는데 객체 단위로 작성하기를 지향 한다" 라는 개념이야.

자 그러면 "객체 단위로 작성한다" 라는 것이 무엇인지 예를 들어볼게.

객체 지향 프로그래밍의 핵심 개념

OOP 는 우리의 인간세계가 이루어진 형태와 비슷하게 만들어져 있어.

내가 이 책의 처음부터 지금까지 계속 강조하는 내용이 있었지?

프로그래밍은 인생에서 일어나는 반복적인 일들을 정형화, 자동화 시켜놓은 것.
인생에서 반복적으로 일어나는 정형화된 모든 것들은 모두 프로그램으로 만들 수 있어.

'객체 지향 프로그래밍'은 위에 내가 말했던 내용과 일맥상통하는 개념이야.
'객체(Object)' 라는 것은 우리의 현실과 상상속에 존재하는 모든 것(Object) 을 말해.
학교, 학생, 선생님, 부모님, 자녀, 동물, 강아지, 고양이, 책상, 의자, 화장실, 시계, 휴대폰, 배터리, 전등, 자동차, 사과, 바나나, 물, 컵, 공기, 산소, 질소, 우주선, 블랙홀, 태양, 지구 등등..
생명이 있든 없든, 눈에 보이든 안보이든, 실존하든 상상하든 상관 없어.
네가 원하는게 무엇이든, 구체적으로 상상할 수만 있다면, 그 모든 것을 OOP 로 코딩할 수 있어.

OOP 는 '게임세계' 를 생각하면 아주 쉬워.
게임에는 위에서 설명한 모든 것들이 다 객체로 하나하나 구현되어 있거든.
게임의 캐릭터가 있고, HP, MP, 직업이 있고, 스킬이 있고, 착용한 아이템, 공격력, 방어력 등이 있지. 이 모든 것들이 모두 객체야. 게임을 하면 완전 자연스럽게 받아들여지지? 다시 한 번 말하지만, OOP 는 우리의 인간세계가 이루어진 형태와 비슷하게 만들어져 있어. 그래서 너무나 자연스럽지.

OOP 의 객체(Object) 에는
1. 상태값 (property, 프로퍼티)
2. 행동 (method, 메소드)
라는 2가지 핵심개념이 존재해.
상태값(property)행동(method) 을 꼭 기억해줘.
상태값과 행동이라는 2가지 개념으로 나만의 세계를 구현 할 수 있으니까 말이야!

이제 구체적인 이야기를 해볼게.
게임내에 유저사과장수 가 있다고 생각해보자.

유저사과장수상태값(property) 에는 [이름, , 사과갯수] 가 공통적으로 있고,
유저행동(method) 에는 [구매하기] 가 있으며,
사과장수행동(method) 에는 [홍보하기] 가 있다고 해보자.

사과장수홍보하기 행동 을 사용해서 "맛있는 사과, 1개에 150원!" 이라고 홍보를 하고있어.
유저구매하기 행동 을 사용해서 사과장수로부터 사과 3개를 구매했어.

이로서 유저 상태값사과갯수 상태값 은 구매한 만큼 변하게 되고,
사과장수 상태값사과갯수 상태값 도 역시 판매한 만큼 변하게 되지.

이것을 파이썬의 OOP 로 구현해보면 다음과 같아. 조금 길게 느껴지겠지만 별거 없고, 꼭 손으로 직접 코딩해보길 바래. 이 코드에서 배우는게 많을거야.

class User:       # 유저 클래스
    name = None   # 유저의 이름
    money = None  # 유저의 돈
    apple = None  # 유저의 사과갯수

    def __init__(self, name, money, apple):  # 유저 class 의 생성자
        # self 변수는 클래스 내부에 자체적으로 항상 존재하는 '자기 자신' 을 가리키는 변수
        # 클래스의 모든 메소드는 자동으로 첫번째 파라미터로 self 를 받도록 설계되어있음
        self.name = name    # 이름 넣어주기
        self.money = money  # 돈 넣어주기
        self.apple = apple  # 사과갯수 넣어주기

    def buy(self, user, thing, price, amount):  # '구매하기' 행동 함수
        user.money += price * amount  # 상인의 돈 증가
        self.money -= price * amount  # 나의 돈 감소
        setattr(user, thing, getattr(user, thing) - amount)  # 상인의 사과갯수 감소
        setattr(self, thing, getattr(self, thing) + amount)  # 나의 사과갯수 증가


class Merchant:   # 사과장수 클래스
    name = None   # 사과장수의 이름
    money = None  # 사과장수의 돈
    apple = None  # 사과장수의 사과갯수

    def __init__(self, name, money, apple):  # 사과장수 class 의 생성자
        self.name = name    # 이름 넣어주기
        self.money = money  # 돈 넣어주기
        self.apple = apple  # 사과갯수 넣어주기

    def say(self):  # '홍보하기' 행동 함수
        print("\n[홍보] 맛있는 사과, 1개에 150원!\n")
        # 역슬래시(\)와 n 으로 줄 바꾸기
        # 이것을 '개행문자(escape character)' 라고 함


user = User("철수", 500, 0)            # 유저의 생성자로 instance 생성
merchant = Merchant("영희", 1000, 20)  # 사과장수의 생성자로 instance 생성

print("구매하기 전 상태값\n")
print("유저의 이름: " + user.name)
print("유저의 돈: " + str(user.money))
print("유저의 사과갯수: " + str(user.apple))
print()  # 아무것도 안넣으면 빈 줄을 출력함
print("사과장수의 이름: " + merchant.name)
print("사과장수의 돈: " + str(merchant.money))
print("사과장수의 사과갯수: " + str(merchant.apple))

merchant.say()  # 상인의 홍보
user.buy(merchant, "apple", 150, 3)  # 상인으로부터 사과를 150원에 3개 '구매하기' 행동 함수실행!

print("구매한 이후 상태값\n")
print("유저의 이름: " + user.name)
print("유저의 돈: " + str(user.money))
print("유저의 사과갯수: " + str(user.apple))
print()
print("사과장수의 이름: " + merchant.name)
print("사과장수의 돈: " + str(merchant.money))
print("사과장수의 사과갯수: " + str(merchant.apple))

위 코드를 실행하면 아래와 같은 결과가 나올거야.

정확하게 우리가 예상한 대로 로직이 구현됐지?
객체(Object) 단위의 상호작용으로 프로그램이 동작하는 것.
이게 바로 Object-Oriented Programming(OOP) 이라는거야!

앞에서 Java 는 객체지향프로그래밍(OOP, Object-Oriented Programming) 언어의 대명사 라고 했었고, 우리가 다루는 Python 과 JavaScript 도 마찬가지로 모두 객체지향프로그래밍이 가능한 언어라고 했지?

OOP 는 특정 언어에 국한된 개념이 아니라, 프로그래밍 로직을 설계하는데 사용하는 '방법론', '개념' 일 뿐이고, OOP의 개념을 따르면 OOP가 되는거고, 따르지 않으면 OOP가 아닌거야.
다만 Java 의 경우, 언어 자체가 OOP 로 코딩하기를 권장하도록 설계가 되어있어서 그 대명사가 된거지.

이제 OOP 의 전체적인 개념을 이해했을테니, 조금 더 깊게 들어가보자!

클래스 메소드 내의 self 라는 첫번째 파라미터 이름은 그냥 단순하게 변수명일 뿐이기 때문에, 변수명을 꼭 self 라고 할 필요는 없고, abc 등으로 해도 아무런 문제가 없어요.
하지만 전세계의 거의 모든 파이썬 프로그래머들이 클래스 메소드의 첫번째 파라미터 이름을 self 로 하는 것이 코딩의 규칙(Coding convention)으로서 굳어져있기 때문에, 보편적으로 self 라는 이름을 사용해요. 결론적으로 클래스 메소드의 첫번째 파라미터로 들어오는것은 항상 '인스턴스 자기 자신' 이며, 파라미터 이름은 무엇을 사용해도 문제없어요.

JavaScript 전체 코드

/**
 * Python 과 비교해서 달라진 점:
 * 클래스 상단에 프로퍼티를 선언하지 않고 constructor 내에서 입력
 * 인스턴스를 생성할 때에는 생성자 앞에 new 키워드 사용
 */

class User {  // 유저 클래스
    constructor(name, money, apple) {  // 유저 class 의 생성자
        this.name = name;    // 유저의 이름 넣어주기
        this.money = money;  // 유저의 돈 넣어주기
        this.apple = apple;  // 유저의 사과갯수 넣어주기
    }

    buy(user, thing, price, amount) {  // '구매하기' 행동 함수
        user.money += price * amount;  // 상인의 돈 증가
        this.money -= price * amount;  // 나의 돈 감소
        user[thing] -= amount;  // 상인의 사과갯수 감소
        this[thing] += amount;  // 나의 사과갯수 증가
    }
}

class Merchant {  // 사과장수 클래스
    constructor(name, money, apple) {  // 사과장수 class 의 생성자
        this.name = name;    // 사과장수의 이름 넣어주기
        this.money = money;  // 사과장수의 돈 넣어주기
        this.apple = apple;  // 사과장수의 사과갯수 넣어주기
    }

    say() {  // '홍보하기' 행동 함수
        console.log("\n[홍보] 맛있는 사과, 1개에 150원!\n");
        // 역슬래시(\)와 n 으로 줄 바꾸기
        // 이것을 '개행문자(escape character)' 라고 함
    }
}


const user = new User("철수", 500, 0);            // 유저의 생성자로 instance 생성
const merchant = new Merchant("영희", 1000, 20);  // 사과장수의 생성자로 instance 생성

console.log("구매하기 전 상태값\n");
console.log("유저의 이름: " + user.name);
console.log("유저의 돈: " + user.money);
console.log("유저의 사과갯수: " + user.apple);
console.log();  // 아무것도 안넣으면 빈 줄을 출력함
console.log("사과장수의 이름: " + merchant.name);
console.log("사과장수의 돈: " + merchant.money);
console.log("사과장수의 사과갯수: " + merchant.apple);

merchant.say();  // 상인의 홍보
user.buy(merchant, "apple", 150, 3);  // 상인으로부터 사과를 150원에 3개 '구매하기' 행동 함수실행!

console.log("구매한 이후 상태값\n");
console.log("유저의 이름: " + user.name);
console.log("유저의 돈: " + user.money);
console.log("유저의 사과갯수: " + user.apple);
console.log();
console.log("사과장수의 이름: " + merchant.name);
console.log("사과장수의 돈: " + merchant.money);
console.log("사과장수의 사과갯수: " + merchant.apple);

Java 전체 코드

Java 의 클래스에 생성자를 선언하는 방법은 클래스명과 완전히 동일한 이름의 메소드를 만드는거야.
생성자는 new 키워드와 함께 클래스의 인스턴스를 생성하는 역할만 하기 때문에 별도의 return 타입을 작성하지 않아.
아래 소스코드의 User 클래스와 Merchant 클래스의 생성자를 보면 이해가 될거야.

public class Main {
    public static void mian(String[] args) {

        User user = new User("철수", 500, 0);  // 유저의 생성자로 instance 생성
        Merchant merchant = new Merchant("영희", 1000, 20);  // 사과장수의 생성자로 instance 생성

        System.out.println("구매하기 전 상태값\n");
        System.out.println("유저의 이름: " + user.name);
        System.out.println("유저의 돈: " + user.money);
        System.out.println("유저의 사과갯수: " + user.apple);
        System.out.println();  // 아무것도 안넣으면 빈 줄을 출력함
        System.out.println("사과장수의 이름: " + merchant.name);
        System.out.println("사과장수의 돈: " + merchant.money);
        System.out.println("사과장수의 사과갯수: " + merchant.apple);

        merchant.say();  // 상인의 홍보
        user.buy(merchant, "apple", 150, 3);  // 상인으로부터 사과를 150원에 3개 '구매하기' 행동 함수실행!

        System.out.println("구매한 이후 상태값\n");
        System.out.println("유저의 이름: " + user.name);
        System.out.println("유저의 돈: " + user.money);
        System.out.println("유저의 사과갯수: " + user.apple);
        System.out.println();
        System.out.println("사과장수의 이름: " + merchant.name);
        System.out.println("사과장수의 돈: " + merchant.money);
        System.out.println("사과장수의 사과갯수: " + merchant.apple);

        return;
    }
}

class User {  // 유저 클래스

    public String name = "";
    public int money = 0;
    public int apple = 0;

    public User(String name, int money, int apple) {  // 유저 class 의 생성자
        this.name = name;    // 유저의 이름 넣어주기
        this.money = money;  // 유저의 돈 넣어주기
        this.apple = apple;  // 유저의 사과갯수 넣어주기
    }

    public void buy(Merchant merchant, String thing, int price, int amount) {  // '구매하기' 행동 함수
        merchant.money += price * amount;  // 상인의 돈 증가
        this.money -= price * amount;  // 나의 돈 감소
        if (thing == "apple") {
            merchant.apple -= amount;  // 상인의 사과갯수 감소
            this.apple += amount;  // 나의 사과갯수 증가
        }
    }
}

class Merchant {  // 사과장수 클래스

    public String name = "";
    public int money = 0;
    public int apple = 0;

    public Merchant(String name, int money, int apple) {  // 사과장수 class 의 생성자
        this.name = name;    // 사과장수의 이름 넣어주기
        this.money = money;  // 사과장수의 돈 넣어주기
        this.apple = apple;  // 사과장수의 사과갯수 넣어주기
    }

    public void say() {  // '홍보하기' 행동 함수
        System.out.println("\n[홍보] 맛있는 사과, 1개에 150원!\n");
        // 역슬래시(\)와 n 으로 줄 바꾸기
        // 이것을 '개행문자(escape character)' 라고 함
    }
}
profile
🔥 from Abstraction to Realization

0개의 댓글