본문 바로가기

Language/Java

[OOP] 객체지향 프로그래밍의 개념과 특징

728x90
객체 지향 프로그래밍(Object Oriented Programming)이란?
  • 프로그래밍에서 필요한 데이터를 추상화시켜 상태와 행위를 가진 객체를 만들고, 그 객체들 간의 유기적인 상호 작용을 통해 로직을 구성하는 프로그래밍 방법

객체 지향 프로그래밍의 장점과 단점
  • 장점
    • 코드 재사용 용이
      • 클래스와 객체를 재사용하거나 상속을 통해 기존 코드의 기능을 확장하는 것이 가능
      • 또한, 다형성을 통해 동일한 인터페이스를 사용하여 다양한 객체에 대해 작업을 수행 가능
    • 유지 보수가 쉬움
      • 절차 지향 프로그래밍에서는 코드를 수정해야할 때 일일이 찾아 수정해야하는 반면 객체 지향 프로그래밍에서는 캡슐화와 모듈화 덕분에 클래스와 객체 단위로 변경을 적용할 수 있어 유지보수가 상대적으로 용이
    • 대형 프로젝트에 적합
      • 클래스 단위로 모듈화시켜서 개발할 수 있으므로 대형 프로젝트처럼 여러 명, 여러 회사에서 프로젝트를 개발할 때 업무 분담이 쉬움
  • 단점
    • 처리 속도가 상대적으로 느림
    • 객체를 생성하고 메모리에 할당하므로, 메모리 사용량이 증가할 수 있음
    • 설계 시 많은 시간과 노력이 필요
    • 러닝 커브가 높음
    • 과도한 추상화나 객체 간의 복잡한 관계는 코드의 가독성을 떨어뜨릴 수 있음

클래스(Class)와 인스턴스(객체, Object)
  • 클래스(Class)
    • 어떤 문제를 해결하기 위해 관련된 속성(데이터)과 기능(메서드)를 모아둔 설계도
    • 객체를 만들기 위한 청사진
      Ex) 자동차 설계도(속성 : 바퀴 수, 색상, 엔진 / 기능 : 가속, 정지)
  • 인스턴스(객체)
    • 클래스에서 정의한 속성과 행동을 실제 메모리에 할당하여 만든 구체적인 객체
    • Ex) 자동차 설계도를 토대로 만든 실제 자동차
  • 1개의 클래스(설계도)원하는 만큼의 인스턴스(자동차) 생성 가능
  • 클래스 추상적 개념이지만, 인스턴스 메모리에 존재하는 구체적 데이터
  • 각각의 인스턴스는 독립적으로 동작
    Ex) A 자동차 : 빨간색 / B 자동차 : 파란색

객체 지향 프로그래밍의 특징 4가지
추상화(Abstraction)
  • 불필요한 세부 사항을 숨기고 중요한 정보만을 드러냄
  • 객체의 공통된 속성(데이터)나 기능(메서드)을 추출하여 정의하는 것
  • 멤버 필드(속성)는 public static final 생략되어 상수이며, 기능(메서드)public abstract가 생략되어 추상 메서드
    Ex) 현대에서 자동차와 오토바이를 생산한다고 했을 때 공통된 속성으로는 회사 이름, 공통된 기능은 엑셀, 브레이크가 있음
public interface Vehicle {
    public static final String company = "현대";
    public abstract void go();
    void stop();
}

상속(Inheritance)
  • 기존 클래스(부모 클래스)의 속성(필드)과 기능(메서드)자식 클래스가 물려받아 재사용할 수 있게 하는 개념
  • 상속을 통해 코드의 재사용성을 높이고, 기존 클래스를 확장하여 새로운 기능을 추가하거나, 필요에 따라 메서드를 재정의(오버라이딩) 하여 일부 기능 변경 가능
  • 즉, 부모 클래스의 공통된 기능을 유지하면서, 고유한 속성이나 기능을 추가 가능

자동차와 오토바이 클래스

  • 바퀴 수, 색상 공통으로 가지는 속성(데이터)
  • 전진과 정지 공통으로 가지는 기능(메서드)
  • 공통으로 가지는 것을 묶어 코드의 중복을 제거할 수 있음
public class Car {
    int wheel;
    String color;

    public void go(){
        System.out.println("엑셀을 밟습니다.");
    }

    public void stop(){
        System.out.println("브레이크를 밟습니다.");
    }

    public void booster() {
        System.out.println("부스터를 씁니다.");
    }
}


public class MoterBike {
    int wheel;
    String color;

    public void go(){
        System.out.println("엑셀을 밟습니다.");
    }

    public void stop(){
        System.out.println("브레이크를 밟습니다.");
    }

    public void circus() {
        System.out.println("앞바퀴를 듭니다.");
    }
}

Vehicle 클래스 상속

public class Vehicle {
    int wheel;
    String color;

    public void go(){
        System.out.println("엑셀을 밟습니다.");
    }

    public void stop(){
        System.out.println("브레이크를 밟습니다.");
    }
}

public class Car extends Vehicle{
    public void booster() {
        System.out.println("부스터를 씁니다.");
    }
}

public class MoterBike extends Vehicle{
    @Override
    public void go() {
        System.out.println("엑셀을 좀 더 강하게 밟습니다.");
    }

    public void circus() {
        System.out.println("앞바퀴를 듭니다.");
    }
}

다형성(Polymorphism)
  • 하나의 인터페이스나 부모 클래스를 통해 여러 자식 클래스의 객체를 다룰 수 있는 것을 의미
  • 코드는 더 유연해지고, 같은 메서드를 호출하더라도 해당 객체의 타입에 맞는 동작을 하게 할 수 있음
  • 예를 들어, 이동 수단을 컬렉션으로 묶어서 관리를 하려고 할 때 자료형을 Car와 MoterBike로 선언을 했을 경우에는 두 개의 타입이 같지 않아 컬렉션에 저장할 수가 없음
  • 하지만, Car와 MoterBike가 상속 받은 상위 클래스(Vehicle)을 사용한다면, 컬렉션으로 관리도 가능할 뿐더러 각각의 클래스에 맞는 기능(메서드)가 동작하게 됨
    • 이를 업캐스팅이라고 하는데 주의할 점부모 클래스(Vehicle)에 없는 하위 클래스 고유의 기능은 사용할 수 없어 다운 캐스팅을 하여 사용해야 함
    • 단, 다운 캐스팅은 필요한 경우에만 신중히 사용하며, 안전한 타입 변환을 위해 instanceof 사용 권장
public static void main(String[] args) {
    Car car = new Car();
    MoterBike moterBike = new MoterBike();

    // List<Car, MoterBike> vehicleList = new ArrayList<>();
}
public static void main(String[] args) {
    List<Vehicle> vehicleList = new ArrayList<>();
    vehicleList.add(new Car());
    vehicleList.add(new MoterBike());
    vehicleList.get(0).go();
    vehicleList.get(1).go();
    // vehicleList.get(0).booster(); // 에러
    ((Car)vehicleList.get(0)).booster();
    
	// Result
	// 엑셀을 밟습니다.
	// 엑셀을 좀 더 강하게 밟습니다.
	// 부스터를 씁니다.
}

캡슐화(Encapsulation)
  • 클래스 내부의 데이터(속성)와 메서드(기능)외부에서 직접 접근하지 못하도록 보호하여, 각 객체의 독립성과 안정성 보장
  • 이로 인해 외부 코드가 객체 내부의 데이터에 무분별하게 접근하거나 수정하지 못하게 하고, 객체의 무결성과 안전성을 유지
  • 필요한 경우에만 객체의 데이터를 외부에 노출하여 접근 가능

캡슐화 방법 2가지

  • 접근 제어자(Access Modifiers)
    • 클래스의 속성과 메서드접근 제어자를 설정하여 외부에서의 접근 범위를 제어 가능
    • Ex) 속성을 private로 설정하면 외부에서 해당 속성에 직접 접근이 불가하고 오직 해당 클래스 내에서만 접근이 가능
    • 이렇게 설정된 접근 제어자는 클래스 외부로부터 특정 멤버의 접근을 제한하여 정보 은닉을 강화하고, 클래스의 데이터를 보호
접근 제어자 클래스 내 패키지 내 다른 패키지의 하위 클래스 패키지 외 설명
private O X X X 동일 클래스 내에서만 접근 가능
default O O X X 동일 패키지 내에서만 접근 가능
protected O O O X 동일 패키지 + 다른 패키지의 하위클래스 접근 가능
public O O O O 제한 없음
package package1;

class Test {
    public static void main(String[] args) {
        SuperClass superClass = new SuperClass();
//        System.out.println(superClass.a); // 클래스 외부이기 때문에 에러
        System.out.println(superClass.b);
        System.out.println(superClass.c);
        System.out.println(superClass.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);
    }
}
package package2;

import package1.SuperClass;

class SubClass extends SuperClass {
    public void printEach() {
//        System.out.println(a); // private - 동일 클래스 아님
//        System.out.println(b); // default - 동일 패키지 아님
        System.out.println(c); // protected - 상속 받았기 때문에 가능
        System.out.println(d);
    }
}

public class Test {
    public static void main(String[] args) {
        SuperClass superClass = new SuperClass();
//        System.out.println(superClass.a); // private
//        System.out.println(superClass.b); // default
//        System.out.println(superClass.c); // protected - 다른 패키지의 경우 상속 받지 않아 불가능
        System.out.println(superClass.d);
    }
}

 

  • Getter/Setter 메서드:
    • private로 감춘 속성을 외부에서 읽거나 변경해야 할 때, Getter와 Setter 메서드를 통해 안전하게 접근
    • Getter 메서드속성 값을 반환하며, Setter 메서드속성 값을 설정할 때 유효성 검사를 포함할 수 있어 데이터 무결성을 보장
    • 이렇게 제공된 접근 메서드를 통해 클래스의 내부 구현을 숨기고, 외부에는 필요한 정보만 제공하는 방식을 유지 가능
public class Person {
    // 접근 제어자를 이용하여 외부로부터 데이터를 보호
    private String name;
    private int age;

    // Getter 메서드를 통해 외부에서 name에 접근
    public String getName() {
        return name;
    }

    // Setter 메서드를 통해 age를 설정하면서 유효성 검사도 수행 가능
    public void setAge(int age) {
        if (age > 0) { // 나이는 양수여야 한다는 검증
            this.age = age;
        }
    }
}

참고 자료

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

728x90