포스트

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

공부 배경

“자바는 객체 지향 프로그래밍 언어다.”

내가 알고 있는 자바의 대표적인 설명이다. ‘객체를 지향해 프로그래밍 한다.’ 이렇게 단순하게 알고 있지, 그 특징에 대해서는 잘 몰랐다. 검색해서 찾아보니 설명 글 들만 읽어서는 이해하기가 어려웠다. ‘초등학생? 적어도 내 동생 또래인 중학생 정도의 학생들이 봐도 이해할 수 있는 설명이 있었으면 좋겠다.’ 생각 하며 검색을 하던 중, 내가 바라던 사진과 예시들을 잘 활용하여 설명된 좋은 글이 있어, 이를 참고하여 정리하며 공부해 보았다.


객체란?

객체객체 지향 프로그래밍의 가장 기본적인 단위이자 시작점이다.

“모든 실재(實在)하는 대상”

“우리가 보고 느끼고 인지할 수 있는 그 모든 것”

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

객체 지향 프로그래밍(Object-Oriented-Programming, OOP)란 여러 독립 부품(객체)들의 조합으로 파악하고자 하는 컴퓨터 프로그래밍의 패러다임이다.

‘객체 지향적으로 프로그래밍한다’ : 어떤 프로그램의 일부분에 해당하는 작은 부품, 즉 객체를 먼저 만들고 이렇게 만들어진 여러 객체들을 조립해서 하나의 완성된 프로그램을 만든다.


객체지향 프로그래밍의 장점

1. 객체 지향적 설계를 통해서 프로그램을 보다 유연하고 변경이 용이하게 만들 수 있다.

2. 객체 지향적 원리를 잘 적용해 둔 프로그램은 각각의 객체들이 각자의 독립적인 역할을 가지기 때문에 코드의 변경을 최소화하고 유지보수를 하는 데 유리하다.

3. 코드의 재사용을 통해 반복적인 코드를 최소화하고, 코드를 최대한 간결하게 표현 할 수 있다.

4. 객체 지향 프로그래밍은 실제 우리가 보고 경험하는 세계를 최대한 프로그램 설계에 반영하기 위한 지속적인 노력을 통해 발전해 왔기 때문에, 보다 인간 친화적이고, 직관적인 코드를 작성하기에 용이하다.


객체 지향 프로그래밍의 4가지 특징

image

1. 추상화(Abstraction)

“사물이나 표상을 어떤 성질, 공통성, 본질에 착안하여 그것을 추출하여 파악하는 것”

“공통성과 본질을 모아 추출”

객체 지향 프로그래밍에서 의미하는 추상화는 객체의 공통적인 속성과 기능을 추출하여 정의하는것을 의미한다.

image

예시로 자동차(car)오토바이(MotorBike)는 모두 이동수단(Vehicle)이며 전진과 후진을 할 수 있다는 공통점을 가진다. 자바 문법 요소를 사용하면, 자동차(car)오토바이(MotorBike)하위 클래스(Sub Class), 이동수단(Vehicle)상위 클래스(Super Class)이다. moveForward()moveBackward()공통적인 메서드(기능)이다. 이 외에도 공통적인 변수(속성)도 선언 가능하다.

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

이동수단(Vehicle) 인터페이스
1
2
3
4
5
public interface Vehicle{
    void start();
    void moveForward();
    void moveBackward();
}

자동차(car)오토바이(MotorBike)공통적인 기능을 추출하여 이동수단(Vehicle) 인터페이스에 정의한다.

인터페이스 : “서로 다른 두 시스템, 장치, 소프트웨어 따위를 서로 이어주는 부분 또는 그런 접속 장치”

객체 지향적 설계에 있어서 인터페이스는 어떤 객체의 역할만을 정의하여 객체들 간의 관계를 보다 유연하게 연결하는 역할을 담당한다. 어떤 객체가 수행해야 하는 핵심적인 역할만을 규정해두고, 실제적인 구현은 해당 인터페이스를 구현하는 각각의 객체들에서 하도록 프로그램을 설계하는 것을 의미한다.

자동차(Car) 클래스
1
2
3
4
5
6
7
8
9
10
public class Car implements Vehicle{
    @Override
    public void moveForward(){
        System.out.println("자동차가 앞으로 전진합니다");
    }
    @Override
    public void movebackward(){
        System.out.println("자동차가 뒤로 후진합니다");
    }
}
오토바이(MotorBike) 클래스
1
2
3
4
5
6
7
8
9
10
public class MotorBike implements Vehicle{
    @Override
    public void moveForward(){
        System.out.println("오토바이가 앞으로 전진합니다");
    }
    @Override
    public void movebackward(){
        System.out.println("오토바이가 뒤로 후진합니다");
    }
}

자동차(car)오토바이(MotorBike)이동수단(Vehicle) 인터페이스에 정의한 공통된 역할각각의 클래스의 맥락에 맞게 구현했다.

이것을 객체 지향 프로그래밍에서는 역할과 구현의 분리라고 한다.

객체 지향 프로그래밍에서는 보다 유연하고 변경에 열려있는 프로그램을 설계하기 위해 역할과 구현을 분리하는데, 여기서 역할에 해당하는 부분이 인터페이스를 통해 추상화될 수 있습니다.


2. 상속(Inheritance)

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

추상화의 연장선에서, 상속은 클래스 간 공유될 수 있는 속성과 기능들을 상위 클래스로 추상화 시켜 해당 상위 클래스로부터 확장된 여러 개의 하위 클래스들이 모두 상위 클래스의 속성과 기능들을 간편하게 사용할 수 있도록 합니다.

클래스들 간 공유하는 속성과 기능들을 반복적으로 정의할 필요 없이 딱 한 번만 정의해두고 간편하게 재사용할 수 있어 반복적인 코드를 최소화하고 공유하는 속성과 기능에 간편하게 접근하여 사용할 수 있도록 합니다.

image

빨간색으로 표시된 속성과 기능들은 자동차와 오토바이의 공통적인 부분들이고, 푸른색으로 표시된 부분들은 그렇지 않은 부분들이다.

이동수단(Vehicle) 클래스
1
2
3
4
5
6
7
8
9
10
11
12
13
public class Vehicle{
    String model;
    String color;
    int wheels;
    
    void moveForward(){
        System.out.println("전진합니다.");
    }
    void moveBackward(){
        System.out.println("후진합니다.");
    }
}

자동차(Car) 클래스
1
2
3
4
5
6
7
public class Car extends Vehicle{
    boolean isConvertible;
    
    void openWindow(){
        System.out.println("모든 창문을 엽니다.");
    }
}
오토바이(MotorBike) 클래스
1
2
3
4
5
6
7
8
9
10
11
public class MotorBike extends Vehicle{
    boolean isRaceble;
    
    @Override
    public void moveForward(){
        System.out.println("오토바이가 앞으로 전진합니다");
    }
    void stunt(){
        System.out.println("오토바이로 묘기를 부립니다.");
    }
}
Main 실행 클래스
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Main{
    public static void main(String[] args){
        
        // 객체 생성
        Car car = new Car();
        MotorBike motorBike = new MotorBike();
        
        // car 객체의 속성 정의
        car.model = "테슬라";
        car.color = "빨강색";
        
        System.out.println("나의 자동차는 " + car.color + " " + car.model + "입니다.");
        
        // 객체들의 기능 실행
        car.moveForward();
        motorBike.moveForward();
        motorBike.moveBackward();
    }
}
1
2
3
4
5
출력값
나의 자동차는 빨강색 테슬라입니다.
전진합니다.
오토바이가 앞으로 전진합니다.
후진합니다.

자동차(car)오토바이(MotorBike)클래스의 공통적인 변수(속성)과 메서드(기능)들을 추출(추상화)하여 이동수단(Vehicle)상위 클래스에 정의하였고, extends키워드를 통해 각각의 하위 클래스로 확장하여 해당 기능과 속성들을 매번 반복적으로 정의해야 하는 번거로움을 제거했다.또한, 공통적인 코드의 변경이 있는 경우 상위 클래스에서 단 한 번의 수정으로 모든 클래스에 변경 사항이 반영될 수 있도록 만들었다.

오토바이(MotorBike) 클래스처럼 각각의 클래스의 맥락에 맞게 메서드 오버라이딩(method overriding)을 사용하여 내용을 재정의할 수도 있다.

상속의 경우 상위 클래스의 속성과 기능들을 하위 클래스에서 그대로 받아 사용하거나 오버라이딩을 통해 선택적으로 재정의하여 사용할 수 있는 반면, 인터페이스를 통한 구현은 반드시 인터페이스에 정의된 추상 메서드의 내용이 하위 클래스에서 정의되어야 한다.

결론적으로, 상속 관계의 경우 인터페이스를 사용하는 구현에 비해 추상화의 정도가 낮다고 할 수 있습니다. 인터페이스가 역할에 해당하는 껍데기만 정의해두고, 하위 클래스에서 구체적인 구현을 하도록 강제하는 것에 비해, 상속 관계의 경우 상황에 따라 모든 구체적인 내용들을 정의해두고 하위 클래스에서는 그것을 단순히 일부만 가져다가 재사용할 수 있습니다.


3. 다형성

다형성(多形性)이란 한자 이름 그대로 어떤 객체의 속성이나 기능이 상황에 따라 여러 가지 형태를 가질 수 있는 성질을 의미한다.

image

객체 지향에서 다형성은어떤 객체의 속성이나 기능이 그 맥락에 따라 다른 역할을 수행할수 있는 객체 지향의 특성을 의미한다.

앞서 언급한 메서드 오버라이딩오버로딩도 다형성의 한 중요한 예시지만, 객체 지향의 맥락에서 이것보다 더 중요한 다형성의 정의는 이것이다.

객체 지향 프로그래밍에서 다형성이란 한 타입의 참조변수를 통해 여러 타입의 객체를 참조할 수 있도록 만든 것을 의미한다. 좀 더 구체적으로, 상위 클래스 타입의 참조변수로 하위 클래스의 객체를 참조할 수 있도록 하는 것이다.

이동 수단(Vehicle)예시로 들어보면, 이동 수단(Vehicle)자동차(Car)가 될 수도, 오토바이(MotorBike)가 될 수도 있다. 다르게 표현해보면, 자동차는 자동차이다. 라는 명제와 자동차는 이동수단이다.라는 명제는 모두 참이다.

객체 지향 프로그래밍에서 다형성이란 앞서 설명한 이동 수단 과 같은 넓은 범위의 타입, 즉 상위 클래스 타입의 참조 변수로 그것과 관계있는 하위 클래스들을 참조할 수 있는 능력이다.

Main 실행 클래스
1
2
3
4
5
6
7
8
9
10
11
public class Main{
    public static void main(String[] args){
        
        // 원래의 객체 생성 방식
        Car car = new Car();
        MotorBike motorBike = new MotorBike();
        
        // 다형성을 활용한 객체 생성 방식 
        Vehicle car2 = new Car();
    }
}

위 코드를 통해, 상위클래스 타입의 참조변수로 하위클래스 객체를 참조하는 것의 의미를 조금 더 구체적으로 이해할 수 있다.

왜 다형성을 활용한 방식이 유용하지?

형성을 활용하면 여러 종류의 객체를 배열로 다루는 일이 가능해진다.

1
2
3
4
5
6
7
8
9
10
11
public class Main{
    public static void main(String[] args){
    	Vehicle vehicles[] = new Vehicle[2];
        vehicles[0] = new Car();
        vehicles[1] = new Car();
        
        for (Vehicle vehicle : vehicles){
            System.out.println(vehicle.getClass()); // 각각의 클래스를 호출해주는 메서드
        }
    }
}
1
2
3
출력값
class Car
class MotorBike

다형성을 활용하면 하나의 타입만으로 여러 가지 타입의 객체를 참조할 수 있어 보다 간편하고 유연하게 코드를 작성하는 것이 가능해진다.

다른 예를 살펴보기 위해, 새로운 Driver클래스를 정의하겠다.

Driver 클래스
1
2
3
4
5
6
7
8
9
10
11
public class Driver{
    void driver(Car car){
        car.moveForward();
        car.moveBackward();
    }
    
    void drive(MotorBike motorBike){
        motorBike.moveForward();
        motorBike.moveBackward();
    }
}
Main 실행 클래스
1
2
3
4
5
6
7
8
9
10
11
public class Main{
    public static void main(String[] args){
        
        Car car = new Car();
        MotorBike motorBike = new MotorBike();
        Driver driver = new Driver();
        
        driver.drive(car);
        driver.drive(motorBike);
    }
}
1
2
3
4
5
출력값
전진합니다.
후진합니다.
오토바이가 앞으로 전진합니다.
후진합니다

Driver 클래스의 코드는 매우 간단하다. 매개변수로 자동차(Car)오토바이(MotorBike)객체를 전달받아 운전하는 것이다. 이렇게 하나의 객체가 다른 객체의 속성과 기능에 접근하여 어떤 기능을 사용할 때, 우리는 A클래스는 B클래스에 의존한다라고 표현합니다.

Driver클래스는 자동차(Car), 오토바이(MotorBike)와 직접적인 관계를 가지고 있는데, 이러한 경우를 객체들간의 결합도가 높다고 표현한다.

image

하지만 이렇게 결합도가 높은 상태는 객체 지향적인 설계를 하는 데 매우 불리하다. 이동 수단(Vehicle)자동차(Car)오토바이(MotorBike) 말고도 수 십, 수 백개면, 똑같은 코드를 수 십, 수 백 번 작성해야 할 것이다.

또한, 만약 새로운 상황이 발생해, 오토바이(MotorBike)가 아닌 (MotorCycle)클래스로 변경되야 하는 경우에는 Driver클래스 안에 매개변수로 전달되는 참조변수의 타입과 참조 변수를 수정해야 할 것이다.

코드가 많아질수록 이 작업은 아주 고되고 힘든 작업이 될 수 밖에 것이다.

이런 맥락에서, 객체 지향 프로그래밍은 추상화, 상속, 그리고 다형성의 특성을 활용하여 프로그래밍을 설계할 때 역할구현을 구분하여 객체들 간의 직접적인 결합을 피하고, 느슨한 관계 설정을 통해 보다 유연하고 변경이 용이한 프로그램 설계를 가능하게 만들었다. 이 부분이 객체 지향 프로그래밍의 핵심이다.

이동수단(Vehicle) 인터페이스
1
2
3
4
public interface Vehicle{ // 이동 수단의 역할 정의
    void moveForward();
    void moveBackward();
}
자동차(Car) 클래스
1
2
3
4
5
6
7
8
9
10
public class Car implements Vehicle{
    @Override
    public void moveForward(){
        System.out.println("자동차가 앞으로 전진합니다");
    }
    @Override
    public void movebackward(){
        System.out.println("자동차가 뒤로 후진합니다");
    }
}
오토바이(MotorBike) 클래스
1
2
3
4
5
6
7
8
9
10
public class MotorBike implements Vehicle{
    @Override
    public void moveForward(){
        System.out.println("오토바이가 앞으로 전진합니다");
    }
    @Override
    public void movebackward(){
        System.out.println("오토바이가 뒤로 후진합니다");
    }
}
Driver 클래스
1
2
3
4
5
6
public class Driver{
    void driver(Vehicle vehicle){
        vehicle.moveForward();
        vehicle.moveBackward();
    }
}
Main 실행 클래스
1
2
3
4
5
6
7
8
9
10
11
public class Main{
    public static void main(String[] args){
        
        Car car = new Car();
        MotorBike motorBike = new MotorBike();
        Driver driver = new Driver();
        
        driver.drive(car);
        driver.drive(motorBike);
    }
}
1
2
3
4
5
출력값
자동차가 앞으로 전진합니다
자동차가 뒤로 후진합니다
오토바이가 앞으로 전진합니다
오토바이가 뒤로 후진합니다

한눈에 봐도 코드의 중복이 사라지고, 코드가 훨씬 간결해졌다는 사실을 알 수 있다.

이동수단(Vehicle) 인터페이스를 통해 이동 수단의 역할을 추상화하고, 각각 자동차(Car) 클래스와 오토바이(MotorBike) 클래스에서 기능들을 구현하고 있습니다.

핵심은Driver클래스의 drive() 메서드로 전달되는 매개변수의 타입을 상위 클래스인 인터페이스 타입 이동수단(Vehicle) 로 변경한 것이다. 다형성의세례를 받은 drive() 메서드의 매개변수로 인터페이스를 구현한 객체라면 무엇이든 전달이 될 수 있게 되었다.

image

Driver 클래스가 자동차(Car) , 오토바이(MotorBike) 클래스와 각각과 직접적으로 연결되지 않고 이동수단(Vehicle)인터페이스를 통해 간접적으로 연결되어 결합도가 낮아졌다.

Driver 클래스는 더 이상 각각의 클래스 내부의 변경이나 다른 객체가 새롭게 교체되는 것을 신경 쓰지 않아도 인터페이스에만 의존하여 수정이 있을 때마다 코드 변경을 하지 않아도 된다.

image

비유로 표현하면, 운전자가 운전하는 법을 매번 새롭게 배우지 않아도 변경된 이동 수단 이용하는데 아무런 문제가 발생하지 않는다. 이것이 바로 지금까지 학습한 추상화다형성의 특성을 활용한 역할과 구현의 구분이자, 보다 유연하고 변경이 용이한 소프트웨어 설계를 가능하게 하게 하는 객체 지향 프로그래밍의 꽃이라 할 수 있다.

물론 아직 모든 문제가 해결된 것은 아니다.

Main 실행 클래스
1
2
3
4
5
6
7
8
9
10
11
public class Main{
    public static void main(String[] args){
        
        Vehicle car = new Car(); // 높은 결합도
        Vehicle motorBike = new MotorBike(); // 높은 결합도
        Driver driver = new Driver();
        
        driver.drive(car);
        driver.drive(motorBike);
    }
}

실행 클래스의 코드에서 객체를 생성할 때, new Car()new MotorBike() 처럼 객체에 직접적으로 의존하고 있어서, 해당 객체를 다른 객체로 변경할 시 코드의 변경이 불가피하다.

이 문제를 해결하기 위해 등장한 것이 바로 의존관계 주입(dependency injection)이라 부르는 스프링 프레임워크의 핵심적인 개념입니다. 이는 이후 스프링 프레임 워크의 주요 기능 및 특징에서 다룰 예정이다.


4. 캡슐화(Encapsulation)

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

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

우리가 아플 때 한 번씩 먹게 되는 캡슐 약을 떠올려보면, 우리는 캡슐 안에 어떤 색의 내용물이 있는지, 또 어떤 성분의 약이 들어있는지 알 수 없다. 또한, 그 안의 내용물은 캡슐을 통해서 외부로부터 오염되지 않고 안전하게 보호될 수 있다.

자바의 캡슐화도 이와 같다고 할 수 있다. 즉, 외부로부터 클래스에 정의된 속성과 기능들을 보호하고, 필요한부분만 외부로 노출될 수 있도록 하여 각 객체 고유의 독립성과 책임 영역을 안전하게 지키고자 하는 목적이 있다.

자바 객체 지향 프로그래밍에서 캡슐화를 구현하기 위한 방법은 크게 두 가지가 있다.

1. 접근제어자(access modifiers)를 활용

2. getter/setter 메서드 활용


1. 접근제어자 활용

근제어자는 클래스 또는 클래스의 내부의 멤버들에 사용되어 해당 클래스나 멤버들을 외부에서 접근하지 못하도록 접근을 제한하는 역할을 한다.

자바에는 public,default , protected, private총 4가지 종류의 접근 제어자가 있다.

image

package 1의 SuperClass 클래스
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package package1;

class Test{
    public static void main(String[] args){
        SuperClass superClass = new SuperClass();
        // System.out.println(parent.a); //동일 클래스가 아니기 때문에 에러 발생.
        System.out.println(parent.b);
        System.out.println(parent.c);
        System.out.println(parent.d);
    }
}

public class SuperClass{
    private int a = 1;
    int b = 2;
    protected int c = 3;
    public int d = 4;
    public void printEach(){
        System.out.println(a);
        System.out.println(b);
        System.out.println(c);
        System.out.println(d);
    }
}
1
2
3
4
출력값
2
3
4
package 2의 Test2 클래스
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package package2;

import package1.SuperClass;

class SubClass extends SuperClass{
    public void printEach(){
        //System.out.println(a); //에러 발생
        //System.out.println(b); //에러 발생
        System.out.println(c); // 다른 패키지의 하위 클래스이므로 가능
        System.out.println(d);
    }
}
public class Test2{
    public static void main(String[] args){
        SuperClass parent = new SuperClass();
        
        //System.out.println(parent.a); //public 을 제외한 모든 호출 에러
        //System.out.println(parent.b);
        //System.out.println(parent.c);
        System.out.println(parent.d);
    }
}
1
2
출력값
4

2. getter/setter

Car 클래스
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class Car{
    private String model;
    private String color;
    private int wheels;
    
    public String getModel(){
        return model;
    }
    
    public void setModel(String model){
        this.model = model;
    }
    
    public String getColor(){
        return color;
    }
    
    public void setColor(String color){
        this.color = color;
    }
    
    public int getWheels(){
        return wheels;
    }
    
    public void setWheels(String wheels){
        this.wheels = wheels;
    }
    
}

모든 속성값들이 private 접근 제어자로 선언되어 있고, getter/setter 메서드의 접근제어자만이 public 으로 열려있다. 따라서 선택적으로 외부에 접근을 허용할 속성과 그렇지 않을 속성을 getter/setter 메서드를 통해 설정해줄 수 있다.

캡슐화가 어떻게 객체 지향의 핵심적인 이점과 연결될 수 있는지 코드 예제를 통해 알아보겠다.

Car 클래스
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class Car{
    private String model;
    private String color;
    
    public Car(String model, String color){
        this.model = model;
        this.color = color;
    }
    
    private void startEngine(){ // 캡슐화
        System.out.println("시동을 겁니다.")
    }
    
    private void moveForward(){ // 캡슐화
        System.out.println("자동차가 앞으로 전진합니다.")
    }
    
    private void openWindow(){ // 캡슐화
        System.out.println("모든 창문을 엽니다.")
    }
    
    public void operate(){ //Driver 클래스에서 정의된 메서드를 추출
        startEngin();
        moveForward();
        openWindow();
    }
}
Driver 클래스
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Driver{
    private String name;
    private Car car;
    
    public Driver(String name, Car car){
        this.name = name;
        this.car = car;
    }
    
    public String getName(){
        return name;
    }
    
    public void drive(){
        car.operate(); // Car 클래스에 있는 메서드를 단순하게 호출
    }
}
Main 실행 클래스
1
2
3
4
5
6
7
8
public class Main{
    public static void main(String[] args){
        Car car = new Car("테슬라 모델X", "레드");
        Driver driver = new Driver("김코딩", car);
        
        driver.drive();
    }
}
1
2
3
4
출력값
시동을 겁니다.
자동차가 앞으로 전진합니다.
모든 창문을 엽니다.

Driver 클래스가 메서드를 하나하나 호출하지않고, operate() 메서드로 한번에 묶어 자동차(Car) 클래스로 옮겨두었고, Driver 클래스에서는 내부 동작을 전혀 신경쓰지 않아도 단순히 operate() 메서드를 호출하여 사용하고 있다.

operate() 메서드 내부의 메서드들은 외부에서 호출되어 사용할 일이 없으므로 접근 제어자를 모두 private으로 변경해주었다. 정리하면, 자동차(Car)클래스와 관련된 기능들은 온전히 Car 에서만 관리되도록 하였고, 불필요한 내부 동작의 노출을 최소화하였다. 이제 Driver 클래스의 입장에서는 더 이상 자동차(Car) 클래스의 내부 로직을 알지 못하고, 알 필요도 없어졌다.

이렇게 캡슐화를 활용하면, 객체 내부의 동작의 외부로의 노출을 최소화하여 각 객체의 자율성을 높이고, 이를 통해 객체 간 결합도를 낮추어 앞서 설명한 객체 지향의 핵심적인 이점을 잘 살리는 방법으로 프로그램을 설계하는 일이 가능하다.

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

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.