정의

속성과 기능을 포함한 프로그램 단위로 프로그램의 구성 요소를 객체화하는 것. 객체의 핵심은 기능을 제공하는 것이며, 기능들을 오퍼레이션이라고 부른다.

 

인터페이스: (코딩에서 인터페이스 말고)

객체가 제공하는 모든 오퍼레이션 집합을 객체의 인터페이스라고 부른다. 객체 사용을 위한 명세 의미

 

메시지

오퍼레이션의 실행을 요청하는 것을 메시지를 보낸다라고 표현. 자바나 파이썬에서 메서드를 호출하는 것이 메시지를 보내는 과정에 해당된다.

 

책임

객체가 자신이 제공하는 기능으로써 정의된다는 것은, 객체는 어떤 기능에 대한 책임을 진다는 것이다.

객체마다 이러한 책임의 크기가 작을수록 좋다. 즉, 하나의 객체가 제공하는 기능이 적은 것이 좋다.

하나의 객체에 너무 많은 기능이 포함되어서 그 객체의 책임이 커지는 경우에, 그 기능과 관련된 데이터들이 전부 한 객체로 몰리게 된다. 또한 기능들이 그 데이터를 공유하는 방식으로 프로그래밍이 될 수도 있는데, 이는 절차 지향 방식과 다를 것이 없다. 이를 잘 표현한 것이 SRP이다.

 

의존성

한 객체가 다른 객체에 의존한다는 것은, 특정 객체에서 다른 객체의 메소드를 호출하거나 하는 것을 의미한다. 이러한 의존은, 꼬리에 꼬리를 물며 전파될 수도 있는데, 결국 자기 자신에게까지 영향을 미치게 되는 것을 순환 의존이라고 한다. 이는 DIP 방식으로 해결한다.

 

캡슐화

객체가 내부적으로 기능을 어떻게 구현하는지를 감추는 것이다. 내부 구현이 변경되더라도, 그 기능을 사용하는 코드는 영향을 받지 않도록 해야 한다. 내부 구현 변경의 유연함을 가져다주는 기법이 캡슐화이다.

OOP에서는 캡슐화를 통해, 어떤 한 곳의 변화가 다른 곳에 미치는 영향을 최소화 한다.

 

이때 두 개의 규칙이 있다.

1. Tell, Don't ask -> 데이터를 읽으려고 하지 말고 기능을 실행하라는 것이다. 즉 데이터를 읽더라도 메서드를 통해 데이터를 반환하도록 구현하는 것. 데이터를 읽는 것은 데이터를 중심으로 코드를 작성하게 만드는 원인이 된다.

 

2. 데미테르 법칙

메서드에서 생성한 객체의 메서드만 호출

파라미터로 받은 객체의 메서드만 호출

필드로 참조하는 객체의 메서드만 호출

 


객체 지향 설계 과정

1. 제공할 기능을 세분화하고, 기능을 적절한 객체에 구현(할당)한다.

2. 필요 데이터를 객체에 추가한다.

3. 그 데이터를 활용하는 기능을 추가한다.

4. 이때 최대한 캡슐화하여 구현한다.

5. 객체 간에 메시지를 어떻게 주고받을지 결정한다.


OOP의 전반적인 장점

 

OOP 방식으로 코드를 작성하면, 사전에 작성한 코드에 대한 재사용성이 높아지게 된다. 자주 사용되는 로직(기능, 코드)를 라이브러리 혹은 모듈화 해서 사용할 수 있으며, 이것이 사전에 제대로 만들어졌다는 가정하에 신뢰성을 확보할 수 있다.

 

또한 라이브러리를 각종 예외 상황에 대비하여 잘 만들어두었다면, 컴파일 단계에서 에러를 잡아낼 수 있기 때문에 버그 발생이 줄어든다? -> (이 부분은 피부로 잘 와 닿지 않는다)

 

아무튼 라이브러리화 해서 사용하면, 그것을 가져다 쓰는 개발자는, 그 라이브러리가 어떻게 작동하는지 내부적인 작동 원리를 모르고도 그 기능을 가져다 쓸 수 있기 때문에, 생산성이 높아진다.

 

또한 객체 단위로 코드를 나눠서 작성하기 때문에, 유지 보수와 디버깅에 용이하다.

 

또한 데이터 모델링 시에 객체와 매핑하는 것이 수월하기 때문에, 요구사항을 보다 명확하게 파악하여 프로그래밍할 수 있다. -> (이 부분도 명확히 와 닿지 않음)

 


 

OOP의 단점(문제점)

 

객체 간의 정보 교환이 모두 메시지 교환을 통해 일어나는데, 이는 시스템에 많은 overhead를 준다.

하지만 이 부분은 하드웨어의 발전으로 상당 부분 보완되었다고 한다.

 

치명적인 단점은, 함수형 프로그래밍 패러다임의 등장 배경을 통해서 알 수 있다. 문제는 객체가 상태를 갖는다는 것이다. 어떤 변수가 존재하고, 그 변수를 통해서 객체가 예측할 수 없는 상태를 갖게 되어 애플리케이션 내부에서 버그를 발생시킨다는 것이다. 이러한 문제로, 최근 함수형 패러다임이 주목받고 있다.

 

상속을 통한 재사용의 단점

1. 상위 클래스의 변경이 어렵다

많은 클래스가 상속하고 있는 상위 클래스가 변경되면, 하위 클래스에 영향이 퍼지게 된다.

 

2. 기능 확장을 하면서 클래스의 개수가 증가한다. -> 근데 이걸 피하려면 하나의 클래스 안에 많은 기능을 넣어야 하는데, 충돌하는 거 아닌가?

 

상속에서 나오는 문제를 해결하는 것이 조립(Composition)

단점: 런타임 구조가 복잡해진다.

상속보다 구현이 어렵다.

구조 / 구현이 복잡하더라도, 변경의 유연함이 상속보다 크기 때문에 먼저 고려된다.

 

상속을 사용할 때는, 재사용의 관점이 아니라 기능 확장의 관점에서 적용해야 한다.


객체 지향적 설계 원칙

1. SRP(Single Responsibility Principle) : 단일 책임 원칙

  클래스는 단 하나의 책임을 가져야 한다. 또한 클래스를 변경하는 이유는 단 하나의 이유이어야 한다.

 

2. OCP(Open-Closed Principle) : 개방 - 폐쇄 원칙

  확장에는 열려 있어야 하고, 변경에는 닫혀 있어야 한다.

 

3. LSP(Liskov Substitution Principle) : 리스코프 치환 원칙

  상위 타입의 객체를 하위 타입의 객체로 치환하더라도, 상위 타입을 사용하는 프로그램은 정상 동작해야 한다.

 

4. ISP(Interface Segregation Principle) : 인터페이스 분리 원칙

  인터페이스는 그 인터페이스를 사용하는 클라이언트를 기준으로 분리해야 한다.

 

5. DIP(Dependency Inversion Principle) : 의존 역전 원칙

  고수준 모듈은 저수준 모듈의 구현에 의존해서는 안된다.

 

 

참고한 링크

https://asfirstalways.tistory.com/177https://github.com/JaeYeopHan/Interview_Question_for_Beginner/tree/master/Development_common_sense#%ED%95%A8%EC%88%98%ED%98%95-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D

+ Recent posts