공통/기타

객체 지향? 객체 지향의 특성

반응형

1. OOP 개념에 대해서 설명하세요.

OOP의 배경

OOP 개념이 유행하기 전에, 개발자들은 절차 지향적인 프로그래밍으로 코드를 개발하고 있었습니다.

하지만 점점 프로그램의 규모가 커질수록 유지보수에 어려움이 있는 문제점이 있었는데요. 이러한 문제점을 해결하고자 OOP 패러다임이 탄생하였습니다. (탄생은 아닐지라도 유행..?)

객체란?

먼저 객체란, 실 세계에 존재하는 사물 또는 생명체를 의미합니다.

객체 지향, OOP

OOP란, 객체 지향 프로그램으로, 말 그대로 "객체"를 지향하는 프로그램인데요.

객체지향에서 애플리케이션은 객체들의 집합으로 구성되며, 그 애플리케이션의 기능들은 각 객체들 간의 상호작용을 통해서 구현됩니다. 그리고 각 객체들 사이의 상호작용은 객체들 사이에 주고받는 메시지로 표현된다고 할 수 있습니다.

 

cf) 자바에서는 객체를 표현하기 위해 클래스라는 틀을 제공하고 있으며, 이러한 클래스를 이용해서 실제 메모리 상에 상주시킨 것을 인스턴스라고 표현합니다.


2. 추상화란?

추상화란?

추상화란, 세부적인 내용을 무시한 채 상위 정책을 모델화 한것입니다.

즉 추상화를 사용하면 세부적인 내용(세부 구현 내용)을 무시한 채 상위 정책을 쉽게 간단하게 표현할 수 있습니다.

샘플 코드

a. 인터페이스 Movable

package com.will.domain;

public interface Movable {

	String move();

	String stop();

}

 

  • 인터페이스를 사용함으로써 다음과 같이 실제 구현체 (Car, Bus)의 세부 구현을 공개하지 않고 상위 정책을 모델링할 수 있다.
  • 차후 캡슐화와 연관 있지만, 세부 구현 사항이란 내부적으로 어떤 필드가 있고... 어떻게 구현되었는지를 의미하는 것을 의미합니다... 캡슐화에서 이어서 설명하겠습니다..!

b. 세부 구현체(1) Car

package com.will.domain;

public class Car implements Movable {

	@Override
	public String move() {
		return "자동차 출발....";
	}

	@Override
	public String stop() {
		return "자동차 멈춰..!";
	}

}

 

c. 세부 구현체(2) Bus

package com.will.domain;

public class Bus implements Movable {

	@Override
	public String move() {
		return "버스 출발...";
	}

	@Override
	public String stop() {
		return "버스 멈춰....!";
	}

}

 


3. 캡슐화란

데이터와 기능을 객체 내부로 묶는 것으로, 더 나아가 외부에서의 접근을 통제할 수 있는 접근 제어 개념이 있습니다.

이렇게 객체 내부에 대한 접근을 통제하는 이유는 객체를 좀 더 자율적인 존재로 만들기 위해서입니다.

(클라이언트에서 객체 내부에 실제 구현체에 접근할 수 없도록 방지해서, 내부 구현을 자유롭게 변경할 수 있음.)

package com.will.domain;

public class Car implements Movable {

	private int fuel;

	private Car(int fuel) {
		this.fuel = fuel;
	}

	// 정적 팩토리로, Car의 생성자를 private으로 지정함으로써 상속을 막을 수 있고, 이름을 줄 수 있음.
	// (상속은 캡슐화를 깨는... 좋지 않음... )
	public static Car of(int fuel) {
		return new Car(fuel);
	}

	@Override
	public String move() {
		if (!canMove()) {
			throw new IllegalArgumentException(String.format("연료 (%)가 부족해요...!", fuel));
		}
		fuel -= 1;
		return "자동차 출발....";
	}

	@Override
	public String stop() {
		return "자동차 멈춰..!";
	}

	private boolean canMove() {
		return this.fuel >= 0;
	}

}

 

  • 다음과 같이 필드와 메서드를 자동차 객체 내부에 묶는 것을 캡슐화라고 하며..
  • 캡슐화를 통해서 세부 구현을 외부에서 알 수 없게끔 해서.. 차후 변경에 유연한 코드를 작성할 수 있음.
  • 차후 변경에 유연한 코드라는 것은...
  • 예시로 나중에 자동차가 움직일 때 연료를 10을 사용하며, 움직일 때마다 남은 연료를 출력하는 요구사항이 들어왔다고 하면, 다음과 같이 Car 클래스의 내부 구현을 변경하더라도 이 Car 클래스를 사용하는 클라이언트 객체에서는 영향이 없음을 의미.
package com.will.domain;

public class Car implements Movable {

	private int fuel;

	public Car(int fuel) {
		this.fuel = fuel;
	}

	@Override
	public String move() {
		if (!canMove()) {
			throw new IllegalArgumentException(String.format("연료 (%)가 부족해요...!", fuel));
		}
		fuel -= 10;
		return String.format("자동차 출발.... 현재 남은 연료는 (%s)", fuel);
	}

	@Override
	public String stop() {
		return "자동차 멈춰..!";
	}

	private boolean canMove() {
		return this.fuel >= 0;
	}

}

 


4. 상속

상속이란, 부모 클래스에서 정의한 데이터(필드)와 기능 (메서드)를 물려받는 것을 의미합니다. (재사용의 의미로써)

(참고로 상속에는 구현 상속(implement)과 extends 상속이 존재하는데, extend 상속은 캡슐화를 약화시켜 최대한 합성을 사용하는 식으로 구현해야 합니다.)

public class Car implements Movable { // 구현 상속
	// 생략..
}

Car 클래스 (부모 클래스)

package com.will.domain;

public class Car implements Movable {

	private int fuel;

	public Car(int fuel) {
		this.fuel = fuel;
	}

	@Override
	public String move() {
		if (!canMove()) {
			throw new IllegalArgumentException(String.format("연료 (%)가 부족해요...!", fuel));
		}
		fuel -= 1;
		return "자동차 출발....";
	}

	@Override
	public String stop() {
		return "자동차 멈춰..!";
	}

	private boolean canMove() {
		return this.fuel >= 0;
	}

}

 

SportCar 클래스 (자식 클래스)

package com.will.domain;

// (1)
public class SportCar extends Car {

	public SportCar(int fuel) {
		super(fuel);
	}

	@Override
	public String move() { 
		// (2)
		super.move();
		return "스포츠가 부릉부릉...";
	}

}

 

  • (1) 부모 클래스인 자동차 클래스를 상속받은 스포츠카 클래스.
  • (1) 부모 클래스의 필드(fuel), 메서드(move(), stop())을 상속받아서 사용할 수 있음.
  • (2) 참고로 move()를 오버 라이딩해서, 부모에서 정의한 메서드를 자식 클래스에서 재정의 할 수 있음 (요것도 다형성...)

5. 다형성

다형성이란 클라이언트(객체 개념으로)가 동일한 메시지를 전송하지만, 실제로 어떤 메서드가 호출되는지는 메시지를 수신한 객체의 클래스가 무엇인지에 따라서 달라지는 것을 의미.

이러한 다형성이 가능한 이유는, 지연 바인딩(동적 바인딩) 메커니즘이 있어서입니다.

 

cf) 좀 더 유연한 객체지향 프로그램을 위해서는 컴파일 시점 의존성과 실행 시간 의존성이 달라야 합니다. (DIP 원칙: 변경이 자주 발생하는 객체를 변경이 적은 인터페이스로 분리해서 인터페이스에 의존해야 한다.)

샘플 코드

package com.will;

import com.will.domain.Bus;
import com.will.domain.Car;
import com.will.domain.Movable;

public class Application {

	public static void main(String[] args) {
	 // (1)
		Movable movable = new Car(100);
		System.out.println(movable.move()); // 자동차 출발...
		System.out.println(movable.stop()); // 지동차 멈춰 ....!

		// (2)
		Movable movable2 = new Bus(200);
		System.out.println(movable2.move()); // 버스 출발...
		System.out.println(movable2.stop()); // 버스 멈춰 ...!
	}

}

 

  • (1) 번, (2) 번 타입 모두 타입이 Movable이고 같은 메서드를 호출하지만 (컴파일 시점)
  • 런타임 시점에 실제로 호출되는 메서드는 1번은 Car 객체의 메서드인 반면 2번은 Bus 객체의 메서드가 호출된다.
  • 이렇게 동적 바인딩 메커니즘을 통해서 런타임 시점에 다른 메서드가 호출되는 것을 다형성이라고 한다.
반응형

'공통 > 기타' 카테고리의 다른 글

함수형 프로그래밍이란?  (0) 2021.07.21
링커와 로더  (0) 2021.04.02
SOLID 원칙이란?  (0) 2021.03.18
AOP란? 횡단 관심사? 흩어진 관심사?  (0) 2021.02.16