[oop] OOP란 무엇인가

본 아티클은 [객체지향의 사실과 오해 - 위키북스, 조영호] , [스프링 입문을 위한 자바 객체 지향의 원리와 이해 - 위키북스, 김종민] 의 내용을 참고해 재구성한 내용입니다

객체지향이란 무엇인가

  • 시스템 = 상호작용하는 자율적인 객체들의 공동체로 보는 관점
    • 자율적인 객체 : 상태행위를 함께 지니며 스스로 자기 자신을 책임지는 객체
  • 자율적인 객체들을 이용해 시스템을 분할하는 방법
  • 각 객체는 시스템의 행위를 구현하기 위해 다른 객체와 협력한다
  • 각 객체는 협력 내에서 정해진 역할을 수행하며, 역할은 관련된 책임의 집합이다
  • 객체는 다른 객체와 협력하기 위해 메시지를 전송하고, 메시지를 수신한 객체는 메시지를 처리하는 데 적합한 메서드를 자율적으로 선택

Class != 붕어빵틀

  • 클래스는 분류에 대한 개념이다
  • 그래서, (물론 메타포적인 설명이긴 하지만) ‘class는 붕어빵틀이다’라는 설명은 성립하지 않는다. 이 차이는 사람 김연아 = new 사람() 은 성립하지만 붕어빵틀 붕어빵 = new 붕어빵틀() 은 성립하지 않는다는 것을 생각해보면 명확하다
  • 클래스라는 개념을 실제로 instance화 시킨 것이 객체다

클래스는 개념이고, 객체는 실체다

Class != 객체지향

  • 클래스가 객체 지향의 중요한 구성요소이나, 핵심은 아님
  • 클래스 기반이 아닌 프로토타입 기반의 객체 지향도 있는 것처럼!
  • 중요한 것은 정적인 클래스의 구조가 아니라 메시지를 주고받는 동적인 객체들의 관계
  • 각 객체들의 협력 구조책임을 식별하는 것이 가장 중요

객체지향 패러다임은 지식을 추상화하고, 추상화한 지식을 객체 안에 캡슐화함으로써 실세계 문제에 내재된 복잡성을 관리하려고 한다. 객체를 발견하고 창조하는 것은 지식과 행동을 구조화하는 문제다

캡슐화 (Encapsulation) = 존재하되 감춘다

객체의 자율성 보장을 위해 구현을 외부로부터 감추는 것.

  • 상태와 행위의 캡슐화

    • 객체를 구성하는 상태행위를 묶은 후, 반드시 접근해야만 하는 행위를 골라 공용 인터페이스를 통해 노출
    • 인터페이스와 구현을 분리 -> 객체의 자율성 보장
  • 사적인 비밀의 캡슐화

    • 외부 객체는 다른 객체의 내부를 관찰하거나 간섭할 수 없다
    • 최소한의 커뮤니케이션 통로만 열어놓는데 이것을 공용 인터페이스라 한다
    • 빈번한 변경이 일어나는 불안정한 객체를 안정적인 인터페이스 뒤로 숨긴다

SOLID 원칙의 ISP (Interface Segregation Principle)과 연결된다

우리 객체에 지진이 일어나든 해일이 일어나든 다른 객체가 무관심하게 만들어라. 필요할 때 협력하는 대상으로 존재하되, 존재는 철저히 감춘다

SRP(단일 책임 원칙) vs ISP(인터페이스 분할 원칙)

객체 간의 독립성을 보장하는 데 있어, 두 가지 다른 해결책이 있다

  1. SRP - 단일 책임을 수행하는 다수의 객체로 분할하자!
public class Man() {
	private String 이름;
	private int 몸무게;
	private int 나이;
}

public class Student() {
	public void 출석하기() {}
	public void 공부하기() {}
}

public class Boyfriend() {
	public void 기념일챙기기() {}
	public void 데이트하기() {}
}

public class Soldier() {
	public void 사격하기() {}
	public void 유격하기() {}
}
  1. ISP - 풍성한 상위 객체를 두고 최소화시킨 인터페이스를 구현하자
public class Man() {
	
	private String 이름;
	private int 몸무게;
	private int 나이;

	public void 출석하기() {}
	public void 공부하기() {}
	public void 기념일챙기기() {}
	public void 데이트하기() {}
	public void 사격하기() {}
	public void 유격하기() {}
}

public interface Student {
	public void 출석하기() {}
	public void 공부하기() {}
}

public class ManAsStudent extends Man implements Student {
	@override
	public void 출석하기() {
	}
	@override
	public void 공부하기() {
	}
}

둘 중 하나를 선택하는 개념이 아니라 목적에 따라 혼용할 수 있는 안목이 필요하다.

상속(Inheritance) = 재사용 + 확장

상위 클래스의 특성을 하위 클래스에서 상속하고 거기에 더해 필요한 특성을 추가, 확장해서 사용하는 것을 상속이라 한다. 명칭은 inheritance지만 개념은 extends

상속은 크게 두 가지 특성을 가진다

  1. 상위 클래스로 갈수록 추상화, 일반화되고, 하위 클래스로 갈수록 구체화, 특수화된다
  2. 하위 클래스는 상위 클래스다라는 명제가 반드시 성립해야 한다
    • 딸은 아버지다? (X) -> 객체지향에서의 상속이 아니다 아버지 우리아빠 = new 딸(); 은 당연히 성립하지 않는다

    • 펭귄은 동물이다 (O) -> 분류로서의 구체화된 상속 관계가 존재한다 동물 뽀로로 = new 펭귄();은 자연스럽다

클래스의 특성을 상속하는 것이지, 클래스를 상속하는 것이 아니다

우리가 흔히 오해하고 있는 부분은 상속은 is a 관계를 만족해야 한다는 것이다. 하지만 상속이 개념의 확장재사용이라는 관점에서 본다면 is a kind of 가 좀 더 상속의 특성을 명확히 설명한다.

다시 한 번 상속을 정의해본다면

  1. 객체 지향의 상속은 상위 클래스의 특성을 재사용하는 것이다
  2. 객체 지향의 상속은 상위 클래스의 특성을 확장하는 것이다
  3. 객체 지향의 상속은 is a kind of 관계를 만족해야 한다

extends vs implements

  • 상위 클래스는 하위 클래스에게 특성 (속성과 메서드)를 상속해준다
    • 그래서 상속은 개념의 확장(extends) 이다
  • 인터페이스는 클래스가 ‘무엇을 할 수 있다 (is able to)’ 라고 하는 기능의 구현을 강제한다
    • 그래서 인터페이스는 이행의 충족(implements) 이다

생각해보자

  1. 상위 클래스는 하위 클래스에게 물려줄 특성이 많을수록 좋을까, 적을수록 좋을까?
  2. 인터페이스는 구현을 강제할 메서드가 많을수록 좋을까, 적을수록 좋을까?

다형성(Polymorphism) = 변경에 유연함

  • 같은 목적, 같은 코드, 다른 행위
  • 오버라이딩, 오버로딩의 내용은 제쳐두고 이게 왜 필요할까를 생각해본다면
    • 향후 현실 세계 비즈니스 모델의 변경이나 확장 앞에서 유연하게 대처할 수 있고
    • 추상화시켜 구현의 복잡도를 줄일 수 있고
    • 객체가 독립적인 책임을 지게 해 안정성을 확보할 수 있다

상속을 통한 다형성과 인터페이스를 통한 다형성의 목적이 조금 다른데

  • 상속이 같은 종류의 행위확장을 통해 복잡도를 줄이고 클래스의 재사용성을 높여주는 방향의 다형성에 초점을 맞췄다면
  • 인터페이스는 객체의 사용 방법강제시켜 인터페이스가 약속하는 규칙만 implements 한다면 어떤 객체가 와도 상관이 없는 유연성에 조금 더 방점이 찍히는 것 같다
 Date: April 25, 2019
 Tags:  oop

Previous:
⏪ 20190423 TIL

Next:
20190425 TIL ⏩