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

enjoy89·2022년 6월 15일
4
post-thumbnail

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

  • 객체 지향 프로그래밍(OOP)은 컴퓨터 프로그램을 명령어의 목록으로 보는 시각에서 벗어나 여러 개의 독립된 단위, 즉 객체들의 모임으로 파악하고자 하는 것이다.

  • 각각의 객체는 메시지를 주고 받고, 데이터를 처리할 수 있다.

  • 객체(Object)는 실제 우리 주변에 있는 것이 될 수 있는데, 예를 들면 컴퓨터, 책상, 의자, 강아지, 고양이, 사람 등이 해당된다.

  • 객체 지향 프로그래밍은 프로그램을 유연하고 변경이 용이하게 만들기 때문에 대규모 소프트웨어 개발에 많이 사용된다.

  • 여기서 프로그램을 유연하고 변경이 용이하게 만들어준다의 의미는 바로 부품을 중간에 갈아 끼우듯이, 블럭을 조립하듯이 컴포넌트를 쉽고 유연하게 변경하면서 개발할 수 있다는 뜻이다.


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

1. 추상화(Abstraction)

추상화란 객체에서 공통된 속성과 행위를 추출하여 필요한 정보만을 중심으로 객체를 간소화하는 것을 의미한다.

  • 즉, 여러 구체적인 사물들의 공통적인 특징을 파악하고 이들을 하나의 개념(집합)으로 표현하여 다루는 것이다.

  • 예를 들면 자동차 종류 중 BMW, 아우디, 벤츠 등을 하나의 자동차라는 객체로 추상화하여 표현할 수 있다.

  • 이처럼 추상화의 특징을 이용하면 여러 객체들의 핵심적인 부분에만 집중하여 복잡도를 줄일 수 있다.

2. 캡슐화(Encapsulation)

캡슐화는 연관 있는 변수와 메소드(기능)을 그룹화하는 작업을 의미한다.

  • 여기서 객체 지향 설계 원리를 살펴보면,
  1. 응집도(Cohesion)
    • 클래스나 모듈 안의 요소들이 얼마나 밀접하게 관련되어 있는지를 나타낸다.
  2. 결합도(Coupling)
    • 어떤 기능을 실행하는데 다른 클래스나 모듈들에 얼마나 의존적인지를 나타낸다.
  • 바로 높은 응집도와 낮은 결합도를 유지할 수 있도록 설계하는 것이 바람직한데, 바로 이때 캡슐화가 낮은 결합도를 유지할 수 있게 해주는 설계 원리이다.
  • 이는 정보은닉(Information Hiding)과 관련이 있는데 자바에서 접근 제어 지시자 public, privateprotected 중 private을 사용하여 외부로부터 접근을 제한하고 객체 내에서만 접근이 가능하도록 하여 정보를 보호할 수 있게 해주는 것이다.

3. 상속(Inheritance)

상속이란 부모 클래스(상위 클래스)의 기능을 자식 클래스(하위 클래스)에서 물려 받아(상속 받아) 사용할 수 있는 것을 의미한다.

  • 프로그램을 개발하면서 중복되는 기능을 반복하여 작성하지 않고 상속을 이용하여 재사용이 가능하도록 한다면 작업의 생산성을 향상시킬 수 있다.
  • 이때 부모로부터 상속 받은 기능의 변경이 필요한 부분은 다형성을 통해 변경하여 사용할 수 있다.

4. 다형성(Polymorphism)

다형성이란 하나의 타입에 여러 객체를 대입할 수 있는 성질을 의미하는데, 이는 서로 다른 클래스의 객체가 같은 동작의 메시지를 받았을 때 각자의 다른 방식으로 동작하는 능력이라고 할 수 있다.

  • 다형성은 객체 지향 프로그래밍의 핵심이라고 할 수 있다.
    • 다형성이 상속과 연계되어 동작하면 매우 강력한 힘을 발휘한다.
    • 다형성과 일반화 관계는 코드를 간결하게 할 뿐 아니라 변화에도 유연하게 대처할 수 있게 한다.
  • 이는 자바의 오버라이딩(Overriding), 오버로딩(Overloading)의 형태로 제공된다.
  1. 오버라이딩(Overriding): 상위 클래스에 선언되어 있는 메소드를 하위 클래스에서 동일하게 선언하여 사용하는 것
  2. 오버로딩(Overloading): 하나의 클래스 내에서 메소드의 이름이 같고 매개변수의 개수나 타입이 다른 것

오버라이딩 예시

// 부모 클래스
public abstract class Animal {
    public abstract void eat();// 추상 메소드
}

// 자식 클래스 Dog
public class Dog extends Animal {
    @Override
    public void eat() {
        System.out.println("강아지가 밥을 먹는다.");
    }
}

// 자식 클래스 Cat
public class Cat extends Animal {
    @Override
    public void eat() {
        System.out.println("고양이가 밥을 먹는다.");
    }
}
public class Main {
	public static void main(String[] args) {
    	Animal dog = new Dog();// Upcasting
        Animal cat = new Cat();

        dog.eat();// 강아지가 밥을 먹는다.
        cat.eat();// 고양이가 밥을 먹는다.
    }
}
  • 자식인 DogCat 객체의 클래스 타입은 모두 부모 클래스인 Animal로 같다. eat() 메소드는 바로 부모 클래스에서 물려 받아 각각의 자식 클래스에서 재정의(Override)하여 사용하였다.
  • 이때 메인 메소드에서 각각의 자식 클래스의 eat() 메소드를 호출한 결과, DogCat 각자 다른 결과가 나타나는 것을 확인할 수 있다.

오버로딩 예시

public class Main {
// 오버로딩 1public static void print() {
        System.out.println("Hello");
    }
// 오버로딩 2
public static void print(String str) {
        System.out.println("Hello " + str);
    }
    public static void main(String[] args) {
        print();// Hello
        print("World!");// Hello World!
    }
}
  • 하나의 클래스에서 print()로 같은 이름을 가진 메소드가 2개 존재하지만 하나는 매개변수가 없고 하나는 String형으로 매개변수가 존재한다.
  • 즉, 이들은 메소드 명은 같고 매개변수의 개수가 다르므로 오버로딩하여 메소드를 각각 정의할 수 있다.
  • 한 가지 주의할 점은 '리턴 값만' 다른 것은 오버로딩을 할 수 없다는 것이다.

Reference

https://gmlwjd9405.github.io/2018/07/05/oop-features.html

profile
Backend Developer 💻 😺

0개의 댓글