객체지향 4가지 특징 및 개발 5대 원칙 (SOLID)

임성준·2022년 8월 17일
1
post-thumbnail

OOP
Object
Oriented
Programming

프로그램 설계방법론이자 개념의 일종으로 프로그램을 단순히 데이터와 처리 방법으로 나누는 것이 아니라, 프로그램을 수많은 '객체(object)'라는 기본 단위로 나누고 이들의 상호작용으로 서술하는 방식이다.

여기서 객체하나의 역할을 수행하는 '메소드와 변수(데이터)'의 묶음으로 봐야 한다.

✏️ 특징

  • 추상화(Abstraction)
  • 캡슐화(Encapsulation)
  • 상속(Inheritance)
  • 다형성(Polymorphism)

1️⃣  추상화(Abstraction)

  • 💡   물들의 공통적인 특징을 파악해서 이를 하나의 개념(집합)으로 다루는 것

  • 🚨   목적
    여러 관련 클래스를 그룹화 할 수 있으며, 소프트웨어의 설계 및 구현 프로세스 복잡성을 줄여준다.

2️⃣  캡슐화(Encapsulation)

  • 💡   특정 정보를 외부에서 접그하지 못하도록 숨김

  • 🚨   목적
    높은 응집도, 낮은 결합도를 유지하여 유연함과 유지보수성 증가

3️⃣  상속(Inheritance)

  • 💡   부모 클래스가 자손 클래스에게 속성을 물려주는 것

  • 🚨   목적
    코드의 재사용

4️⃣  다형성(Polymorphism)

  • 💡   하나의 객체에 여러 가지 타입을 대입할 수 있으며 이로인해 다양한 결과를 만듬

  • 🚨   목적
    다형성을 사용하면 같은 이름의 속성을 유지함으로서 속성을 사용하기 위한 인터페이스는 유지하고 메소드 이름을 낭비하지 않는다. (코드의 재사용성을 늘려 유지보수가 용이하도록 도와준다.)

개발 5대 원칙   S O L I D

S  - Single Responsibility Principle(SRP)

  • 💡  한 클래스는 하나의 책임만 가져야 한다.

    SRP원리를 적용하면 무엇보다 책임 영역이 확실해지기 때문에 한 책임의 변경에있어서 다른 책임의 변경으로의 연쇄작용이 자유로울 수 있다. 뿐만 아니라 책임을 적절히 분배함으로써 코드의 가독성 향상, 유지보수 용이라는 이점까지 누릴 수 있으며 객체지향 원리의 대전제 격인 OCP원리뿐 아니라 다른 원리들을 적용하는 기초가된다.

  • 🚀  적용 방법

    리팩토링(Refactoring: Improving the Design of Existing Code - Martin Fowler)에서 소개하는 대부분의 위험상황에 대한 해결방법은 직/간접적으로 SRP원리와 관련이 있다. 이는 항상 코드를 최상으로 유지한다는 리팩토링의 근본정신 또한 항상 객체들의 책임을 최상의 상태로 분배한다는 것에서 비롯되기 때문이다.

    1️⃣  여러 원인에 의한 변경 (Divergent change) :
    Extract Class를 통해 혼재된 각 책임을 각각의 개별 클래스로 분할하여 클래스 당 하나의 책임만을 맡도록 하는 것이다. 여기서 관건은 책임만 분리하는 것이 아닌 분리된 두 클래스간의 관계의 복잡도를 줄여 주어야한다. 만약 Extract Class된 각각의 클래스들이 유사하고 비슷한 책임을 중복해서 갖고 있다면 Extract Superclass를 사용할 수 있다. 이는 Extract된 각각의 클래스들의 공유되는 요소를 부모 클래스로 정의하여 부모 클래스에 위임하는 기법이다. 따라서 각각의 Extract Class들의 유사한 책임들은 부모에게 명백히 위임하고 다른 책임들은 각자에게 정의할 수 있다.

    2️⃣   사탄총 수술(Shotgun surgery) :
    Move Field와 Move Method를 통해 책임을 기존의 어떤 클래스로 모으거나, 이럴만한 클래스가 없다면 새로운 클래스를 만들어 해결할 수 있다. 즉 산발적으로 여러 곳에 분포된 책임들을 한 곳에 모으면서 설계를 깨끗하게 하고 응집성을 높이는 작업이다.

O - Open/Closed Principle(OCP)

  • 💡  소프트웨어 요소는 확장에는 열려 있으나 변경에는 닫혀 있어야 한다.

    버틀란트 메이어(Bertrand Meyer)박사는 1998년 객체지향 소프트웨어 설계 라는 책에서 소프트웨어의 구성요소(컴포넌트, 클래스, 모듈, 함수)는 확장에는 열려있고, 변경에는 닫혀있어야 한다고 말했다. 이것은 변경을 위한 비용은 가능한 줄이고 확장을 위한 비용은 가능한 극대화 해야 한다는 의미로, 요구사항의 변경이나 추가사항이 발생하더라도 기존 구성요소는 수정이 일어나지 말아야 하며, 기존 구성요소를 쉽게 확장해서 재사용할 수있어야 한다는 뜻이다. 로버트 C.마티은 OCP는 관리가능하고 재사용 가능한 코드를 만드는 기반이자 OCP를 가능케하는 중요 메커니즘은 추상화와 다형성이라고 설명하고있다. OCP는 객체지향의 장점을 극대화하는 아주 중요한 원리라 할 수 있다.

  • 🚀  적용 방법

  1. 변경(확장)될 것과 변하지 않을 것을 엄격히 구분한다.
  2. 이 두 모듈이 만나는 지점에 인터페이스를 정의한다.
  3. 구현에 의존하기보다 정의한 인터페이스에 의존하도록 코드를 작성한다.

L  - Liskov Substitution Principle(LSP)

  • 💡  프로그램의 객체는 프로그램의 정확성을 깨뜨리지 않으면서 하위 타입의 인스턴스로 바꿀 수 있어야 한다

    LSP는 부모 객체와 이를 상속한 자식 객체가 있을 때 부모 객체를 호출하는 동작에서 자식 객체가 부모 객체를 완전히 대체할 수 있다는 원칙이다. 객체지향 언어에선 객체의 상속이 일어난다. 이 과정에서 부모/자식 관계가 정의된다. 자식 객체는 부모 객체의 특성을 가지며, 이를 토대로 확장할 수 있다. 하지만 이 과정에서 무리하거나 객체의 의의와 어긋나는 확장으로 인해 잘못된 방향으로 상속되는 경우가 생긴다. 리스코프 치환 원칙(LSP)은 올바른 상속을 위해 자식 객체의 확장이 보무 객체의 방향을 온전히 따르도록 권고하는 원칙이다.

  • 🚀  적용 방법

  1. 만약 두 개체가 똑 같은 일을 한다면 둘을 하나의 클래스로 표현하고 이들을 구분할 수 있는 필드를 둔다.
  2. 똑같은 연산을 제공하지만, 이들을 약간씩 다르게 한다면 공통의 인터페이스를 만들고 둘이 이를 구현한다.(인터페이스 상속)
  3. 공통된 연산이 없다면 완전 별개인 2개의 클래스를 만든다.
  4. 만약 두 개체가 하는 일에 추가적으로 무언가를 더한다면 구현 상속을 사용한다.

I   - Interface Segregation Principle(ISP)

  • 💡  특정 클라이언트를 위한 인터페이스 여러 개가 범용 인터페이스 하나보다 낫다.

    구현할 객체에게 무의미한 메소드의 구현을 방지하기 위해 반드시 필요한 메소드만을 상속/구현하도록 권고한다. 만약 상속할 객체의 규모가 너무 크다면, 해당 객체의 메소드를 작은 인터페이스로 나누는 것이 좋다.

  • 🚀  적용 방법

    1️⃣   클래스 인터페이스를 통한 분리
    클래스의 상속을 이용하여 인터페이스를 나눌 수 있다. 이와 같은 구조는 클라이언트에게 변화를 주지 않을 뿐 아니라 인터페이스를 분리하는 효과를 갖는다. 하지만 거의 모든 객체지향 언어에서는 상속을 이용한 확장은 상속받는 클래스의 성격을 디자인 시점에 규정해 버린다. 따라서 인터페이스를 상속받는 순간 인터페이스에 예속되어 제공하는 서비스의 성격이 제한된다.

    2️⃣   객체 인터페이스를 통한 분리
    위임(Delegation)을 이용하여 인터페이스를 나눌 수 있다. 위임이란, 특정 일의 책임을 다른 클래스나 메소드에게 맡기는 것이다. 만약 다른 클래스의 기능을 사용해야 하지만 그 기능을 변경하고 싶지 않다면, 상속 대신 위임을 사용한다.

D - Dependency Inversion Principle

💡  추상화에 의존해야지, 구체화에 의존하면 안된다.

의존성 역전 원칙이란 객체는 저수준 모듈보다 고수준 모듈에 의존해야한다는 원칙이다.

  • 고수준 모듈: 인터페이스와 같은 객체의 형태나 추상적 개념
  • 저수준 모듈: 구현된 객체

  • 🚀  적용 방법

    잘 구조화된 객체지향 아키텍처들은 각 레이어마다 잘 정의되고 통제되는 인터페이스를 통한 긴밀한 서비스들의 집합을 제공하는 레이어들로 구성되어 있다. 이것은 단순히 레이어를 통한 구조화만을 뜻하는 것이 아니라 Transitive Dependency가 발생했을 때 상위 레벨의 레이어가 하위 레벨의 레이어를 바로 의존하게 하는 것이 아니라 이 둘 사이에 존재하는 추상 레벨을 통해 의존해야 할 것을 말하고있다. 이를 통해서 상위레벨의 모듈은 하위레벨의 모듈로 의존성에서 벗어나 그자체로 재사용 되고 확장성도 보장 받을 수 있다. 이를 도식화하면 다음과 같다.

참조 :

profile
오늘도 공부 📖🌙

0개의 댓글