OOP

[OOP] 객체지향의 4대 특징 - 캡슐화, 추상화, 상속, 다형성

태크민 2024. 6. 10. 22:31

캡슐화

연관된 데이터(변수)와 기능(메소드)을 하나로 묶고, 불필요한 요소를 외부에 노출되지 않도록 설계하는 방식을 뜻합니다. 자바에서는 접근 제어자(public, private, default, protected)를 통해 캡슐화를 구현할 수 있습니다.

class Student {
    private int id;
    private String name;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

public class 캡슐화_예제 {
    
    public static void main(String[] args) {
        Student s1 = new Student();

        // 직접적인 접근은 할 수 없다.
        // s1.id = 1;
        // s1.name = "choi";
        
        // public 접근 제어자인 getter와 setter를 통해 값에 접근할 수 있다.
        s1.setId(1);
        s1.setName("choi");
    }
    
}

 

장점

1. 코드의 안정성을 높일 수 있습니다.

  • 캡슐화를 통해 외부에서 객체의 상태를 직접 접근할 수 없기 때문에 객체의 내부 상태가 불변하게 유지됩니다.
  • 이로 인해 예기치 않은 상태 변경을 방지할 수 있으며, 코드의 안정성을 높일 수 있습니다.

2. 객체의 내부 구현을 숨길 수 있습니다.

  • 객체의 내부 동작을 감추고, 필요한 기능만 외부에 노출할 수 있습니다.
  • 외부에서는 객체의 내부 구현을 몰라도 제공된 인터페이스(메서드)만 사용하면 되므로, 코드의 복잡도를 줄일 수 있습니다.
  • 또한, 내부 구현이 변경되더라도 외부 코드에 영향을 주지 않기 때문에 유지보수성이 향상됩니다.
  • 다른 코드와의 의존성이 낮아지므로, 수정이 필요한 경우에도 최소한의 변경으로 시스템 전체에 영향을 주지 않고 유지보수가 용이해집니다.

3. 코드의 재사용성을 높일 수 있습니다.

  • 객체의 내부 구현을 감춤으로써, 다양한 환경에서 객체를 동일한 방식으로 사용할 수 있습니다.
  • 변경이 필요할 경우 객체 내부에서만 수정하면 되므로, 기존 코드를 수정하지 않고도 재사용이 가능합니다.

단점

1. 코드의 이해도가 떨어질 수 있습니다.

  • 캡슐화를 지나치게 하면 객체의 내부 구현을 숨기기 때문에 객체의 내부 동작을 이해하는 데 어려움을 겪을 수 있습니다.

 

추상화

추상화는 클래스들의 공통적인 요소를 뽑아서 상위 클래스를 만들어내는 것입니다.

추상화는 공통적인 속성과 기능을 정의함으로써 코드의 중복을 줄이고, 클래스 간 관계를 효과적으로 설정하고, 유지보수를 용이하게 할 수 있습니다.
자바에서는 추상 클래스와 인터페이스라는 문법 요소를 통해 추상화를 구현할 수 있습니다.

 

장점

1. 코드의 재사용성이 증가합니다.

  • 공통적인 기능을 추출하여 상위 클래스(추상 클래스 또는 인터페이스)로 만들면, 여러 클래스에서 재사용할 수 있습니다.
  • 이를 통해 중복 코드가 줄어들고, 일관된 인터페이스를 제공할 수 있어 코드의 품질이 향상됩니다.

2. 유연한 설계를 가능하게 합니다.

  • 인터페이스나 추상 클래스를 사용하면, 구체적인 구현을 몰라도 상위 개념만을 다룰 수 있습니다.
  • 새로운 기능을 추가하더라도 기존 코드에 영향을 주지 않아, 확장성이 뛰어나고 변경이 용이한 구조를 만들 수 있습니다.
  • 예를 들어, Payment 인터페이스를 만들고 CreditCardPayment, PayPalPayment 등의 클래스를 구현하면,
    새로운 결제 방식을 추가하더라도 기존 코드를 수정하지 않고 확장할 수 있습니다.

3. 객체 간 결합도를 낮추어 코드의 유연성을 증가시킵니다.

  • 추상화를 활용하면 구체적인 구현 대신 인터페이스(추상 클래스)를 참조하므로, 객체 간의 의존성이 줄어듭니다.
  • 즉, 한 객체가 다른 객체의 내부 구조에 의존하지 않기 때문에 설계를 변경하거나 새로운 기능을 추가할 때 영향이 최소화됩니다.

단점

1. 초기 설계가 어렵고, 시간이 많이 걸릴 수 있습니다.

  • 추상화를 제대로 활용하려면 어떤 기능을 공통화할지, 인터페이스를 어떻게 설계할지 고민해야 합니다.

 

상속

상속은 기존의 클래스에 기능을 추가하거나 재정의하여 새로운 클래스를 정의하는 것을 의미합니다. 상속을 이용하면 기존에 정의되어 있는 클래스의 모든 필드와 메서드를 물려받을 수 있습니다. 

 

장점

1. 코드의 재사용성을 높일 수 있습니다.

  • 부모 클래스에서 정의한 속성과 메서드를 자식 클래스에서 그대로 사용하거나, 일부만 수정하여 재사용할 수 있습니다.
  • 이를 통해 중복 코드를 줄이고, 유지보수를 쉽게 할 수 있습니다.
  • 예를 들어, Animal 클래스에서 eat(), sleep() 메서드를 정의하면,
    Dog, Cat 클래스에서 별도로 같은 메서드를 구현하지 않고도 사용할 수 있습니다.

2. 유지보수가 쉬워집니다.

  • 공통된 기능을 부모 클래스에서 관리하기 때문에, 코드를 변경할 때 한 곳만 수정하면 모든 자식 클래스에 반영됩니다.
  • 예를 들어, Vehicle 클래스의 move() 메서드를 수정하면, Car, Bike, Bus 등의 자식 클래스에서도 자동으로 변경된 기능을 사용할 수 있습니다.

3. 객체의 계층 구조를 형성하여 코드의 구조를 명확하게 할 수 있습니다.

  • 상속을 사용하면 객체 간 계층 구조를 만들어 논리적인 관계를 형성할 수 있습니다.
  • 예를 들어, Employee → Manager, Engineer 같은 계층 구조를 형성하면,
    객체 간의 관계를 쉽게 이해할 수 있고, 객체 지향적인 설계가 가능합니다.
  • 클래스 간의 계층적 관계를 구성함으로써 다형성의 문법적 토대를 마련할 수 있습니다.

단점

1. 부모 클래스에 의존성이 생겨 유연성이 떨어질 수 있습니다.

  • 자식 클래스는 부모 클래스에 강하게 의존하기 때문에,
    부모 클래스가 변경되면 자식 클래스도 영향을 받을 가능성이 큽니다.
  • 부모 클래스의 설계가 잘못되었을 경우, 자식 클래스가 불필요한 속성과 메서드를 상속받아야 할 수도 있습니다.

2. 코드의 가독성과 유지보수가 어려울 수 있습니다.

  • 상속 관계가 깊어질수록 코드의 흐름을 파악하기 어려워질 수 있습니다.
  • 여러 단계의 상속을 거친 경우, 어떤 메서드가 어디서 정의되었는지 찾기 어려울 수 있습니다.
  • 잘못된 상속 구조를 사용하면 단순한 코드보다 유지보수가 어려워질 가능성이 있습니다.

 

다형성

다형성은 하나의 객체나 메서드가 여러 가지 다른 형태를 가질 수 있는 것을 뜻합니다.
자바에서는 이러한 다형성을 이용하여 부모-자식 관계의 클래스들이 존재할 때, 부모 클래스로 자식 클래스들을 서로 다르게 동작시킬 수 있습니다. (또는 인터페이스)

 

새, 고양이, 강아지라는 클래스가 있고 모두 cry()라는 울음소리를 낼 수 있는 메서드가 있다고 가정해보겠습니다.
각각의 클래스에서 cry()를 호출하기 위해서는 아래와 같은 코드가 될 것입니다.

class Bird {
    void cry() {
        System.out.println("짹짹");
    }
}

class Cat {
    void cry() {
        System.out.println("야옹");
    }
}

class Dog {
    void cry() {
        System.out.println("멍멍");
    }
}

public class 다형성_예제 {

    public static void main(String[] args) {
        Bird bird = new Bird();
        Cat cat = new Cat();
        Dog dog = new Dog();

        bird.cry(); // 짹짹
        cat.cry(); // 야옹
        dog.cry(); // 멍멍
    }

}

 

하지만, 부모 클래스를 정의한 후 다형성을 활용한다면 아래와 같이 보다 간결한 코드를 만들 수 있습니다.

abstract class Animal {
    void cry() {}
}

class Bird extends Animal {
    void cry() {
        System.out.println("짹짹");
    }
}

class Cat extends Animal {
    void cry() {
        System.out.println("야옹");
    }
}

class Dog extends Animal {
    void cry() {
        System.out.println("멍멍");
    }
}

public class 다형성_예제 {

    public static void main(String[] args) {
        Animal[] animals = new Animal[]{new Bird(), new Cat(), new Dog()};
        for (Animal animal : animals) {
            animal.cry(); // 짹짹, 야옹, 멍멍
        }
    }

}

 

장점

1. 코드의 가독성과 유지보수가 향상됩니다.

  • 다형성을 사용하면 동일한 메서드 이름을 사용하여 다양한 객체를 다룰 수 있기 때문에 코드가 더 직관적이고 읽기 쉬워집니다.
  • 예를 들어, draw() 메서드를 호출하면 Circle, Rectangle, Triangle 등 다양한 도형을 그릴 수 있습니다.
  • 이를 통해 불필요한 코드 중복을 줄이고, 코드의 일관성을 유지할 수 있습니다.

2. 코드의 재사용성과 유지보수성을 높일 수 있습니다.

  • 다형성을 활용하면 여러 개의 비슷한 기능을 개별적으로 구현하지 않고, 하나의 공통된 인터페이스(또는 부모 클래스)로 처리할 수 있습니다.

단점

1. 객체 타입을 정확히 이해해야 합니다.

  • 다형성을 사용하면 부모 타입을 통해 객체를 참조하지만,
    실제 객체가 어떤 타입인지 정확히 이해하지 않으면, 예상과 다른 동작을 할 수도 있습니다.
  • 예를 들어, Animal 타입으로 Dog 객체를 다룰 때, Dog에만 존재하는 메서드를 직접 호출할 수 없습니다.

2. 성능 오버헤드가 발생할 수 있습니다.

  • 다형성을 사용하면 메서드 호출 시 동적 바인딩(Dynamic Binding)이 발생하여 성능이 저하될 수 있습니다.
  • 특히, 다형성이 과도하게 사용되면 JVM(자바의 경우)이 실제 객체의 타입을 매번 확인해야 하므로, 속도가 느려질 수 있습니다.
  • 성능이 중요한 시스템에서는 정적 바인딩(Static Binding)을 고려해야 할 수도 있습니다.

 

 

 

끝.