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

  • 객체지향 프로그래밍은 컴퓨터 프로그램을 명령어의 목록으로 보는 시각에서 벗어나 여러 개의 객체들이 서로 협력하는 형태의 구조를 지향한다

  • 객체는 각자 맡은 책임이 있고, 서로 메시지를 주고받으면서 협력한다.

객체지향의 핵심

  • 역할과 책임 협력은 객체지향의 핵심이다.

  • 객체들이 애플리케이션의 기능을 구현하기 위해 수행하는 상호작용을 협력이라고 한다.

  • 그리고 객체가 협력에 참여하기 위해 수행하는 로직을 책임이라고 부른다.

  • 객체들이 협력 안에서 수행하는 책임들이 모여 객체가 수행하는 역할을 구성한다.

  • 협력

    • 두 객체의 협력에 사용되는 유일한 수단은 메시지 전송이다.

    • 다른 객체에게 도움을 요청하기 위해 메시지를 전송하고, 메시지를 수신한 객체는 메시지를 처리하는 데 적합한 메서드를 자율적으로 선택한다.

  • 책임

    • 협력에 참여하기 위해 객체가 수행하는 행동을 책임이라고 한다.

    • 객체가 책임을 수행하는 방법을 메서드라고 부른다.

    • 책임 주도 설계(Responsibility-Driven Design)란 책임을 수행할 적절한 객체를 찾아 책임을 할당하는 방식으로 설계하는 방법이다.

  • 역할

    • 객체가 어떤 특정한 협력 안에서 수행하는 책임의 집합을 역할이라고 한다.

    • 역할을 통해서 유연하고 재사용 가능한 협력을 얻을 수 있다.

    • 역할은 다른 것으로 교체할 수 있는 책임의 집합이다.

    • 때문에 특정 역할을 수행할 수 있는 객체라면 어떠한 객체로 바꿔 끼워도 상관 없다.

객체지향 특징

추상화

  • 추상화란 객체들의 공통된 특징을 파악하여 하나의 개념으로 다루는 것이다.

  • 객체지향 프로그래밍에서는 추상화를 통해 클래스를 정의하고 객체를 생성한다.

상속

  • 상속은 새로운 클래스가 기존의 클래스의 자료와 연산을 이용할 수 있게 하는 기능이다.

  • 상속을 통해 새로운 클래스를 작성할 때 기존 클래스의 자료와 연산을 수정하지 않고도 확장할 수 있다.

  • 기존의 클래스를 상위 클래스, 부모 클래스라 부르며, 상속을 받는 클래스를 하위 클래스, 자식 클래스, 파생 클래스라 부른다.

  • 상속을 통해 기존의 클래스를 상속받은 하위 클래스를 이용해 프로그램의 요구에 맞추어 클래스를 수정할 수 있고, 클래스 간의 종속 관계를 형성함으로써 객체를 조직화할 수 있다.

다형성

  • 다형성은 하나의 변수명, 함수명 등이 상황에 따라 다른 의미로 해석될 수 있는 것을 의미한다.

  • 일반적으로 오버라이딩과 오버로딩을 말한다.

  • 오버로딩(Overloading)은 같은 이름의 메서드가 매개변수의 타입과 개수를 다르게 하여 정의하는 것을 의미한다.

    public class Calculator {
        public int sum(int a, int b) {
            return a + b;
        }
        
        public int sum(int a, int b, int c) {
            return a + b + c;
        }
        
        public double sum(double a, double b) {
            return a + b;
        }
        
        public int sum(int x, int y) { // 이 메서드는 컴파일 에러가 발생한다.
            return x + y;              // 첫번째 메서드와 메서드의 시그니처가 같기 때문이다.
        }
    }
  • 오버라이딩(Overriding)은 상위 클래스가 가지고 있는 메서드를 하위 클래스가 재정의하여 사용하는 것을 의미한다.

    public class Animal {
        public void move() {
            System.out.println("동물이 움직입니다.");
        }
    }
    
    public class Human extends Animal {
        @Override
        public void move() {
            System.out.println("사람이 두 발로 걷습니다.");
        }
    }

@Override 어노테이션은 컴파일러에게 해당 메서드가 오버라이딩된 메서드임을 알려준다. 만약 해당 메서드가 오버라이딩된 메서드가 아니라면 컴파일 에러를 발생시킨다. 따라서 @Override 어노테이션을 사용하면 코드의 가독성을 높이고, 버그를 방지할 수 있다.

캡슐화

  • 캡슐화는 객체의 상태와 행동을 객체 안에 모으고, 내부 구현을 외부로부터 감추는 것을 말한다.

  • 다시말해 외부에서 알 필요가 없는 부분을 감춤으로써 대상을 단순화하는 추상화의 한 종류다.

  • 접근 제어자를 private으로 설정하여 캡슐화를 구현할 수 있다.

응집도

  • 응집도는 모듈에 포함된 내부 요소들이 연관돼 있는 정도를 나타낸다.

  • 모듈 내의 요소들이 하나의 목적을 위해 긴밀하게 협력한다면 그 모듈은 높은 응집도를 가진다.

  • 모듈 내의 요소들이 서로 다른 목적을 추구한다면 그 모듈은 낮은 응집도를 가진다.

  • 하나의 변경을 수용하기 위해 다수의 모듈이 함께 변경돼야 한다면 응집도가 낮은 것이고, 하나의 모듈만 변경된다면 응집도가 높은 것이다.

결합도

  • 결합도는 의존성의 정도를 나타내며 다른 모듈에 대해 얼마나 많은 지식을 갖고 있는지를 나타내는 척도이다.

  • 어떤 모듈이 다른 모듈에 대해 너무 자세한 부분까지 알고 있다면 두 모듈은 높은 결합도를 가진다.

  • 어떤 모듈이 다른 모듈에 대해 꼭 필요한 지식만 알고 있다면 두 모듈은 낮은 결합도를 가진다.

  • 결합도가 높으면 함께 변경해야 하는 모듈의 수가 늘어나기에 변경이 어려워진다.

OOP에서 코드의 설계, 유지보수성, 확장성, 이해 용이성을 높이기 위한 5가지 설계 원칙

  • (S) 단일 책임 원칙 - Single Responsibility Principal

    • 하나의 클래스는 하나의 책임(핵심기능)만을 가져야 한다.

    • SRP을 준수할 경우 영향 범위가 축소 되고 변경으로 인한 오류 가능성이 감소한다.

  • (O) 개방 폐쇄 원칙 - Open-Closed Principal

    • 새로운 기능에는 열려 있어야 하지만, 기존 기능의 변경에는 닫혀 있어야 한다.

    • 기존 코드를 변경하여 새로운 기능을 추가할 경우 코드의 안정성이 떨어지므로 기존 코드를 변경하지 않고 새로운 코드를 작성해야 한다.

  • (L) 리스코프 치환 원칙 - Liskov Substitution Principal

    • 부모 클래스의 인스턴스가 자식 클래스의 인스턴스로 대체될 수 있어야 한다.

    • 상속 관계에서 부모 클래스와 자식 클래스의 기능 호환성을 의미한다.

  • (I) 인터페이스 분리 원칙 - Interface Segregation Principal ###

    • 클라이언트는 자신이 필요한 최소한의 인터페이스만 사용해야 한다.

    • 하나에 인터페이스에 많은 기능을 포함할 경우 코드의 결합도가 높아지므로 기능 별로 인터페이스를 정의하고, 필요한 기능만 인터페이스로 제공한다.

  • (D) 의존 역전 원칙 - Dependency Inversion Principal

    • 객체는 구체적인 객체가 아닌 추상화에 의존해야 한다.

    • 예시 - Dependency Injection - Constructor에 Interface를 Parameter로 받는 경우

객체지향의 장단점

  • 장점

    • 변경에 유연하게 대응할 수 있는 구조를 갖게 되어 유지보수하기 쉬워진다

    • 코드의 재사용성을 높일 수 있다.

    • 객체 단위로 코드를 작성하기 때문에 디버깅이 쉽다.

  • 단점

    • 프로그램이 커질 경우 객체간의 관계를 파악하기 어렵다.

    • 규모가 작은 프로젝트에서 객체지향을 적용한다면 오히려 가독성을 저하시킬 수 있다.

    • 추상화와 다형성 등의 기능을 제공하기 위해 런타임에서 추가적인 작업을 수행해야 하기에 일부 경우에는 성능 오버헤드가 발생할 수 있다.


ref

Last updated