본문 바로가기

Java/Java SE, EE

[Java/Java SE, EE] 객체 지향 프로그래밍(OOP)

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

소프트웨어 개발 단계에서 일종의 부품과 같은 객체(Object)를 먼저 만들고, 객체들을 하나씩 조립하는 것을 객체 지향 프로그래밍(OOP, Object Oriented Programmig)라고 부릅니다. 객체들을 조립한다는 것은, 프로그램에서 객체들간의 밀접한 관계를 만들고 상호 작용하게끔 하는 것을 의미합니다.

객체(Object)

객체란 물리적으로 존재하거나 또는 추상적인 개념을 프로그램에서 실체화하는 것을 의미합니다. 예를 들면, 물리적으로 존재하는 자동차, 집, 사람, 옷 등이 될 수 있고 추상적인 개념으로는 보험, 거래, 색상, 계절 등 우리가 생각 할 수 있는 모든 것을 객체화 할 수 있습니다.

객체는 객체가 갖는 성격에 따라서 속성과 동작을 지니게 됩니다(모든 객체가 반드시 속성과 동작을 지녀야 하는 것은 아닙니다). 사람을 예로 들면 나이, 성별, 키, 몸무게와 같은 속성을 지닐 수 있습니다. 사람의 동작으로는 밥을 먹다, 차를 타다, 직장에 출근하다, 잠을 자다 등이 될 수 있습니다.

속성 동작
나이 밥을 먹다
성별 차를 타다
직장에 출근하다
몸무게 등 잠을 자다 등

이러한 객체의 속성과 동작은 프로그램에서 필드(Field)와 함수-메소드(Method)로 구현 할 수 있습니다. 이렇게 객체를 소프트웨어 상에서 필드와 메소드를 구분하여 설계하는 것을 객체 모델링(Object modeling)이라고 부릅니다. 이처럼 자바 프로그램(또한 다른 언어에서도 마찬가지로)의 객체는 필드와 메소드를 갖는 클래스(Class)로 구현합니다.

필드 메소드
나이 밥을 먹다
성별 차를 타다
직장에 출근하다
몸무게 등 잠을 자다 등

상호 작용

앞서 OOP는 객체 간 상호 작용을 기반으로 동작한다고 설명했습니다. 상호 작용이란 객체 간 메소드의 호출입니다. 메소드는 인자-파라미터(Parameter)와 리턴(Return)으로 구성됩니다. 파라미터는 객체 간 상호 작용에서 동작 주체에게 다른 객체의 정보를 알려주기 위함입니다. 리턴은 상호 작용의 결과를 나타냅니다.

예를 들어서 사람과 음식이라는 객체를 정의했을 때, 사람은 '밥을 먹다'라는 메소드에 음식 객체를 파라미터로 전달 할 수 있습니다. 사람 객체는 '밥을 먹다' 메소드에서 파라미터로 받은 음식 객체의 '영양분' 필드에 따라서 자신의 '건강'이나 '포만감' 등의 필드를 조절 할 수 있습니다.

public class Human {
   private int health;
   
   public int eatFood(int _nutrient) {
      health += _nutrient;
      return health;
   }
}

public class Food {
   private int nutrient;
}

public void InteractionExample() {
   Human some_human;
   Food some_food;
   
   some_human.eatFood(some_food.nutrient);
}

이는 사람 객체와 음식 객체가 메소드를 통해서 상호 작용을 하는 과정 중 하나입니다. 메소드에서 상호작용이 완료되면, '밥을 먹다'의 결과로 사람 객체의 변경된 '건강'이나 '포만감' 등의 필드를 리턴하여 이를 프로그램에 출력하거나, 또 다른 상호 작용을 위해 사용 할 수 있을 것입니다.

캡슐화(Encapsulation)

캡슐화란 객체 모델링을 거쳐 객체의 필드 또는 메소드를 외부에 감추는 것을 의미합니다. 외부 객체는 캡슐화 된 객체의 필드나 중요한 메소드에 접근 할 수 없습니다.

위의 사람과 음식 객체의 예시를 다시 살펴보면, 음식 객체는 사람 객체의 '건강'이나 '포만감'같은 필드 정보를 알 필요가 없고, '밥을 먹다'라는 메소드를 직접 호출할 필요가 없습니다. 반대로 사람 객체 역시 음식 객체의 '영양분' 필드에 직접 접근 할 필요가 없습니다. 사람 객체가 '밥을 먹다' 메소드를 호출 할 때, 음식 객체가 자신의 '영양분' 필드 정보를 파라미터에 전달 할 수 있기 때문입니다.

다시 정리하면 객체 간의 상호 작용에서 필드와 메소드를 감추는 것을 캡슐화라고 부릅니다. 앞서 객체는 자바의 클래스로 구현한다고 설명했습니다. 캡슐화는 객체의 필드와 메소드에 접근 제한자(Access modifier)를 사용해 구현합니다.

  • public
  • protected
  • private
  • default

접근 제한자는 클래스, 필드, 메소드의 선언에서 함께 사용됩니다. 접근 제한자는 모든 접근을 허용하는 public부터 시작해서 default로 갈수록 엄격하게 접근을 제한합니다. 이에 대한 자세한 내용은 다른 포스트에서 진행하도록 하겠습니다.

상속(Inheritance)

상속은 부모 객체가 갖는 필드와 메소드를 자식 객체에게 물려주는 것을 의미합니다. 이는 자식 객체가 부모 객체의 필드와 메소드를 사용 할 수 있다는 의미입니다. 부모 객체와 자식 객체의 관계는 자바에서 extends 키워드를 사용하여 표현합니다.

public class ParentClass { 
   protected int some_field;
   protected int some_method() { return 0; }
}

public class ChildClass extends ParentClass { 
   protected int some_child_method() {
   	some_field += 1;
        some_method();
   }
}

상속을 사용하면 객체 모델링 단계에서 중복되는 코드를 줄이고, 동일한 필드와 메소드를 공유하는 클래스를 빠르게 구현 할 수 있습니다. OOP가 개발 속도의 향상과 유지 보수 등에 유리하다는 장점은, 이처럼 상속의 특성으로 인한 것입니다.

다형성(Polymorphism)

다형성은 하나의 객체 타입에 대해서 여러 개의 객체 타입을 대입 할 수 있음을 의미합니다. 다음 예시에서는 자바에서 다형성을 구현하고 있습니다. 예시에서는 부모 객체 변수에 자식 객체를 대입하고, 인자로 전달하고 있습니다. 이 구문이 가능한 이유는 부모 객체가 갖는 필드와 메소드를 상속의 특성으로 인해 모든 자식 객체가 반드시 보유하고 있기 때문입니다.

public class Human { }
public class Soldier extends Human { }
public class Cook extends Human { }

public void PolymorphismExample_0() {
   Human some_human;
   Soldier some_soldier = new Soldier();
   Cook some_cook = new Cook();
   
   some_human = some_soldier;		// Human 변수에 Soldier 객체 대입
   some_human = some_cook;		// Human 변수에 Cook 객체 대입
   some_human = new Human();
   
   PolymorphismExample_1(some_human);	// Human 인자에 Human 객체 대입
   PolymorphismExample_1(some_soldier);	// Human 인자에 Soldier 객체 대입
   PolymorphismExample_1(some_cook);	// Human 인자에 Cook 객체 대입
}

public void PolymorphismExample_1(Human _some_human) { }

반대로 자식 객체의 필드, 변수, 메소드의 인자에는 부모 객체를 전달 할 수 없습니다. 부모 객체는 자식 객체가 갖고 있는 필드와 메소드를 온전히 갖고 있다고 확신 할 수 없기 때문입니다. 이는 허용되지 않는 구문이고, 컴파일 에러를 발생시킵니다.

public class Human { }

public class Soldier extends Human { }

public void PolymorphismExample_0() {
   Human some_human = new Human();
   Soldier some_soldier = some_human; 	// compile error
}