Java

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

dev-rootable 2023. 5. 1. 15:47

📌 객체 지향 프로그래밍이란

 

객체 지향 프로그래밍(OOP)은 컴퓨터 프로그램을 객체들의 모임으로 파악하고자 하는 것으로, 각각의 객체는 서로 협력할 수 있다. 또한, OOP는 상속, 다형성 등으로 다양한 관계를 만들어내기 때문에 프로그램을 유연하고 변경 용이하도록 만들어준다.

 

🔎 객체(Object)란

 

클래스의 인스턴스나 배열

 

넓은 의미로 실세계에 존재하거나 생각할 수 있는 것을 의미한다. 프로그래밍에서 객체는 필드 변수를 통해 속성을 표현하고 메서드를 통해 기능을 표현한다. 이렇게 표현한 설계도를 클래스라고 하며, 이 클래스로 공통 데이터를 가진 객체를 찍어내고, 우리는 이러한 원본 객체에 속성이나 기능을 변경하고 어떤 관계를 추가하면서 객체를 커스터마이징 한다.

 

📌 특징

 

🔎 다형성

실세계 객체를 역할과 구현으로 구분하여 구현하는 개념으로, 예시로 Java에서는 Interface, Overriding, Spring에서는 DI(Dependency Injection) 이 있다.

 

즉, 클라이언트(사용자)는 대상의 역할(인터페이스)만 알면 내부 구조를 몰라도 그리고 내부의 변경 내용을 몰라도 다른 대상으로 교체할 수 있다. 다시 말해서, 역할이라는 큰 특징 아래 구체적인 구현 내용을 담은 다양한 제품을 만들 수 있도록 한 것이 다형성이다.

 

  • 자바에서는 Interface를 구현한 객체 인스턴스를 실행 시점에 유연하게 변경할 수 있다.
  • Client를 변경하지 않고, 서버의 구현 기능을 유연하게 변경할 수 있다.
  • 인터페이스 자체가 변하면 클라이언트, 서버 모두에 큰 변경이 발생하므로, 인터페이스를 잘 설계하는 것이 중요

 

어떤 객체의 속성이나 기능이 상황에 따라 여러 가지 형태를 가질 수 있는 성질

 

🔎 추상화(Abstraction)

 

사전적 의미로 불필요한 세부 사항들은 제거하고 가장 본질적이고 공통적인 부분만을 추출하여 표현하는 것이다.

 

OOP에서 의미하는 추상화는 객체의 공통적인 속성과 기능을 추출하여 정의하는 것을 의미한다.

 

자바에서 추상화를 구현할 수 있는 문법 요소로는 추상 클래스인터페이스가 있다.

 

OOP에서는 역할과 구현의 분리를 통해 프로그래밍을 하는데, 앞서 살펴본 다형성은 구현이고 추상화는 구현의 기반 데이터가 되는 역할의 정의를 의미한다. 이를 통해 OOP는 보다 유연하고 변경에 열려있는 프로그램을 설계할 수 있다.

 

🔎 상속(Inheritance)

 

상속이란 기존의 클래스를 재활용하여 새로운 클래스를 작성하는 자바의 문법 요소를 말한다.

 

이것은 상위 클래스의 확장과 공유를 의미하며, 모든 하위 클래스들은 상위 클래스의 모든 속성과 기능들을 간편하게 사용할 수 있다. 즉, 클래스들 간 공유하는 속성과 기능들을 반복적으로 정의할 필요 없이 딱 한 번만 정의해 두고 간편하게 재사용할 수 있어 반복적인 코드를 최소화하고 공유하는 속성과 기능에 간편하게 접근하여 사용할 수 있다.

 

하지만 상속은 다형성과 차이가 있다. 대표적인 예로 메서드를 재정의하여 사용하는 메서드 오버라이딩이 있다.

 

상속과 다형성 모두 상위, 하위 클래스의 관계를 전제하고 공통적인 속성과 기능을 공유하지만, 상속의 경우 상위 클래스의 속성과 기능들을 하위 클래스에서 그대로 받아 사용하거나 오버라이딩을 통해 선택적으로 재정의하여 사용할 수 있다. 반면, 다형성은 인터페이스에 정의된 추상 메서드를 하위 클래스에서 구현하도록 강제한다. 그래서 상속은 인터페이스를 사용한 구현에 비해 추상화의 정도가 낮다. 또한, 상속은 상황에 따라 모든 구체적인 내용들을 정의해 두고 하위 클래스에서는 그것을 단순히 가져다가 재사용할 수 있다.

 

상속은 상위 클래스의 속성과 기능을 재정의하지 않고 사용하거나 선택적으로 구현할 수 있다.

 

🔎 캡슐화(Encapsulation)

 

클래스 안에 서로 연관 있는 속성과 기능들을 하나의 캡슐로 만들어 데이터를 외부로부터 보호하는 것을 말한다.

 

OOP에서 캡슐화를 하는 이유는 크게 2가지다.

 

  • 데이터 보호 : 외부로부터 클래스에 정의된 속성과 기능들을 보호
  • 데이터 은닉 : 내부의 동작을 감추고 외부에는 필요한 부분만 노출

 

자바 OOP에서 캡슐화는 접근 제어자를 활용하여 구현할 수 있다. 먼저, 접근 제어자는 종류에 따라 외부 클래스, 외부 패키지, 상속 클래스 등 접근 범위를 정할 수 있어 필요한 부분만 외부에 노출할 수 있다.

 

접근 제어자 동일 클래스 동일 패키지 다른 패키지의 자식 클래스 패키지 외 설명
private O X X X 동일 클래스 내에서만 접근 가능
default O O X X 동일 패키지 내에서만 접근 가능
protected O O O X 동일 패키지와 다른 패키지의 자식 클래스에서 접근 가능
public O O O O 모두 허용

 

이렇게 캡슐화를 사용하여 데이터 보호와 은닉뿐만 아니라 객체 간의 결합도도 낮춰준다.

 

예를 들어 A라는 클래스가 아래와 같을 때, B라는 클래스는 A 클래스의 모든 메서드에 접근할 수 있다. 다음 코드를 살펴보자

 

public class A {
    
    private String first;
    private String second;
    
    public A(String first, String second) {
        this.first = first;
        this.second = second;
    }
    
    public void function1() {
        //기능 1
    }
    
    public void function2() {
        //기능 2
    }
    
}

 

public class B {
    
    private int var;
    private A a;
    
    public B(int var, A a) {
        this.var = var;
        this.a = a;
    }
    
    public void method() {
        a.function1();
        a.function2();
    }
    
}

 

위 코드를 보면 B에서 A의 메서드에 직접 접근하여 사용하고 있다. 이렇게 하면 B는 A의 메서드에 변경이 생기면 불가피하게 변경 영향을 그대로 받는다. 그래서 아래와 같이 변경하여 결합도를 낮출 수 있다.

 

public class A {
    
    private String first;
    private String second;
    
    public A(String first, String second) {
        this.first = first;
        this.second = second;
    }
    
    private void function1() {
        //기능 1
    }
    
    private void function2() {
        //기능 2
    }
    
    public void op() {
        function1();
        function2();
    }
    
}

 

public class B {
    
    private int var;
    private A a;
    
    public B(int var, A a) {
        this.var = var;
        this.a = a;
    }
    
    public void method() {
        a.op();
    }
    
}

 

이렇게 접근 제어자를 활용하여 캡슐화를 구현할 수 있다. 이제 B 클래스는 A 클래스의 내부 동작을 알 수 없고, public으로 오픈한 op 메서드를 통해 사용하기만 한다. 따라서, 하나의 객체는 해당 객체의 속성과 기능에 대한 독점적인 책임을 담당하도록 만들고(객체의 자율성), 이를 통해 객체 간의 결합도를 낮게 유지할 수 있다.

 

📌 장점

 

🔎 코드의 재사용성이 높다. -> 생산성이 높다.

 

상속을 통해 기존에 선언된 클래스의 기능을 물려받거나 다형성을 통해 기능을 구체적으로 정의할 수 있기 때문에 기존 코드의 재사용성이 높다.

 

🔎 유지보수가 용이하다.

 

각 객체의 정보를 다른 객체가 디테일하게 알게 된다면 두 객체의 결합도는 상승한다. 이는 서로 분리가 어렵고, 변경 영향이 크다는 것을 의미한다. 그래서 객체지향프로그래밍은 추상화, 캡슐화를 통해 변경 가능성이 높은 구체적인 부분은 숨기고, 변경이 적은 안정적인 부분만 공개하여 결합도를 낮추고 유지보수를 수월하게 한다.

 

🔎 신뢰성이 높은 프로그래밍이 가능하다.

 

캡슐화를 통해 데이터를 은닉, 보호하며 코드의 재사용성이 높아 코드 중복으로 인한 오동작을 방지할 수 있다.

 

📌 단점

 

🔎 실행 속도가 상대적으로 느리다.

 

JVM을 통해 2차 컴파일을 하므로, OS 독립적인 장점이 있지만 JVM을 로딩하고 컴파일하는 시간이 추가된다. 그리고 가비지 컬렉터로 인한 지연 시간도 있다. 하지만 C나 C++ 같은 언어와 비교한 상대적인 속도차이며, JIT 컴파일러 등을 통해 상당히 개선되었다.

 

🔎 설계에 많은 시간이 소요된다.

 

각 객체를 정의하고, 객체 간의 관계를 설정하는 등 설계 단계에서 많은 시간이 소요된다.

 

Reference:

https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%ED%95%B5%EC%8B%AC-%EC%9B%90%EB%A6%AC-%EA%B8%B0%EB%B3%B8%ED%8E%B8/dashboard

 

스프링 핵심 원리 - 기본편 - 인프런 | 강의

스프링 입문자가 예제를 만들어가면서 스프링의 핵심 원리를 이해하고, 스프링 기본기를 확실히 다질 수 있습니다., - 강의 소개 | 인프런

www.inflearn.com

 

https://upcake.tistory.com/418

 

[Java] 도대체 객체가 뭔데? : 객체, 클래스, 인스턴스

1. 개요 개발을 하다 보면 정말 자주 듣고 보고 말하고 쓰게 될 말이 객체입니다. 도대체 객체란 무엇일까요? 오늘 포스트에서는 객체, 클래스, 인스턴스에 대해서 제가 공부한 것을 바탕으로 아

upcake.tistory.com

 

https://www.codestates.com/blog/content/%EA%B0%9D%EC%B2%B4-%EC%A7%80%ED%96%A5-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%ED%8A%B9%EC%A7%95

 

객체 지향 프로그래밍의 4가지 특징ㅣ추상화, 상속, 다형성, 캡슐화 -

객체 지향 프로그래밍은 객체의 유기적인 협력과 결합으로 파악하고자 하는 컴퓨터 프로그래밍의 패러다임을 의미합니다. 객체 지향 프로그래밍의 기본적인 개념과 그 설계를 바르게 하기 위

www.codestates.com