객체지향(2) - 다형성, 캡슐화, 추상화

Yunsung·2024년 12월 28일
post-thumbnail

이전 글에서 작성한 글을 이어서 작성하도록 하겠습니다!!
안보신 분들은 이전 게시글을 보고 와주세요.😊😊

4. 다형성👨‍👩‍👧(Polymorphism)

다형성이란 부모 클래스 타입의 참조 변수로 자식 클래스 객체를 다룰 수 있는 능력을 의미합니다. 다형성은 메서드 오버로딩이나 메서드 오버라이딩을 통해 구현됩니다. 예를 들어, 동물 클래스에서 울다라는 메서드를 정의하고, 이를 상속받은 개와 고양이 클래스에서 각각 다르게 구현할 수 있습니다.

다형성의 특징

1. 부모 타입 참조 변수로 자식 객체를 다룬다.

  • 부모 클래스의 배열에 자식 클래스 객체를 담아 관리할 수 있습니다.

  • 예를 들어, PersonVO[] 배열에 StudentVO, TeacherVO, EmployeeVO 객체를 담아 공통적으로 처리합니다.

    형변환을 해서 부모배열에 값을 넣으면 자동 형변환으로 인해 타입이 부모로 바뀐다.

    PersonVO perAry[] = new PersonVO[10];
    // 부모 타입 배열에 자식 객체를 저장
    perAry[0] = new StudentVO("섭섭이", 20, "서울", "2000");
    perAry[1] = new TeacherVO("김교사", 35, "부산", "JAVA");
    perAry[2] = new EmployeeVO("박직원", 40, "인천", "교육팀");

2. 런타임 시점에서의 객체 타입 확인

  • 배열에 저장된 객체가 실제 어떤 타입인지 확인하려면 instanceof 연산자를 사용.

  • 이를 통해 적절한 다운캐스팅을 수행하여 객체의 고유한 메서드를 호출할 수 있다.

    📢 형변환이된 값을 보기 위해서는 다운캐스팅(명시적 형변환)이 필요하다.
    📢 다형성에서 객체의런타입시점의 타입을 확인할 수 있도록 instanceof를 사용한다.

    // 다운캐스팅을 통해 각 객체의 고유 메서드 호출
    for (PersonVO per : perAry) {
       if (per instanceof StudentVO) {
           System.out.println(((StudentVO) per).stuInfo());
       } else if (per instanceof TeacherVO) {
           System.out.println(((TeacherVO) per).teaInfo());
       } else if (per instanceof EmployeeVO) {
           System.out.println(((EmployeeVO) per).empInfo());
       }
    }

3. 유지보수와 확장성 향상

  • 새로운 자식 클래스가 추가되더라도, 부모 클래스 타입으로 객체를 관리하면 기존 코드 변경 없이 확장이 가능합니다.


5. 캡슐화💊(Encapsulation)

캡슐화란 무엇인가❓❓

캡슐화는 객체의 속성(필드)을 외부에서 직접 접근하지 못하도록 숨기고, 대신 접근 메서드(getter/setter)를 통해 제어하는 것을 의미합니다. 이를 통해 데이터의 무결성을 유지하고 외부의 불필요한 접근이나 변경을 방지할 수 있습니다.

캡슐화의 필요성❗

  1. 데이터 보호: 중요한 데이터에 대한 무분별한 접근을 방지합니다.
  2. 제어된 접근: 데이터를 읽거나 수정할 때 논리적 검증을 추가할 수 있습니다.
  3. 유지보수 용이: 내부 구조 변경 시 외부 코드에 영향을 주지 않고 수정할 수 있습니다.

📚 캡슐화 구현 방법

  1. 필드를 private으로 설정하여 외부에서 직접 접근을 차단합니다.
  2. gettersetter 메서드를 public으로 제공하여 간접적으로 접근합니다.
  3. 필요에 따라 읽기 전용 또는 쓰기 전용으로 설정할 수 있습니다.

캡슐화 에제: 학생 정보 관리

public class Student {
    private String name;
    private int age;

    // 생성자
    public Student(String name, int age) {
        this.name = name;
        setAge(age); // 유효성 검사를 위해 setter 사용
    }

    // getter
    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        if (age > 0) { // 나이는 0 이상이어야 한다는 조건 추가
            this.age = age;
        } else {
            System.out.println("나이는 0보다 커야 합니다.");
        }
    }
}

사용코드

public class Main {
    public static void main(String[] args) {
        Student student = new Student("shimssung", 20);

        System.out.println("이름: " + student.getName());
        System.out.println("나이: " + student.getAge());

        student.setAge(-5); // 유효성 검사로 인해 값 변경 실패
        System.out.println("변경된 나이: " + student.getAge());
    }
}

출력 결과

이름: shimssung
나이: 20
나이는 0보다 커야 합니다.
변경된 나이: 20


6. 추상화(Abstraction)

추상화란 객체가 제공하는 중요한 기능만을 노출하고, 그 이면의 복잡한 구현을 숨기는 것입니다.
예를 들어, 차량이라는 추상 클래스를 만들고, 운전이라는 공통 기능을 정의할 수 있습니다.

추상화의 구현 방식

1. 추상 클래스(Abstract Class)

  • 추상 클래스는 공통적으로 필요한 속성과 메서드를 정의하며, 이를 상속받는 자식 클래스가 구체적인 기능을 구현합니다.
  • 추상 클래스는 객체를 직접 생성할 수 없지만, 변수의 타입으로는 사용 가능합니다.
  • 부모 클래스가 추상 메서드를 포함하면, 이를 상속받는 자식 클래스는 반드시 오버라이딩(구현)을 해야 합니다.
// 추상 클래스 정의
public abstract class Animal {
    public void eating(String food) {
        System.out.printf("%s 를(을) 먹습니다.\n", food);
    }
    public abstract void fly(); // 추상 메서드
}

// 추상 클래스를 상속받는 클래스
public class SuperMan extends Animal {
    @Override
    public void fly() {
        System.out.println("망토로 중력을 이용해서 날아다닙니다.");
    }
}

public class Bird extends Animal {
    @Override
    public void fly() {
        System.out.println("날개로 중력을 이용해서 날아다닙니다.");
    }
}

// 메인 클래스
public class AbstractApp {
    public static void main(String[] args) {
        // 추상 클래스는 타입으로 사용 가능
        Animal[] ary = new Animal[2];
        ary[0] = new SuperMan();
        ary[1] = new Bird();

        for (Animal animal : ary) {
            animal.fly(); // 오버라이딩된 메서드 호출
            animal.eating("쌀");
        }
    }
}

2. 인터페이스(Interface)

  • 인터페이스는 상수와 추상 메서드로만 구성되며, 다중 상속을 지원합니다.
  • 추상 메서드의 집합으로, 클래스의 설계 표준을 정의하는 데 사용됩니다.
  • 추상 클래스와 달리, 클래스 간의 의존성을 더 느슨하게 만들 수 있어 확장성 및 유지보수성이 뛰어납니다.
// 인터페이스 정의
public interface Flyer {
    void fly();
}

// 추상 클래스와 인터페이스를 함께 구현
public class SuperMan extends Animal implements Flyer {
    @Override
    public void fly() {
        System.out.println("망토로 중력을 이용해서 날아다닙니다.");
    }
}

public class Bird extends Animal implements Flyer {
    @Override
    public void fly() {
        System.out.println("날개로 중력을 이용해서 날아다닙니다.");
    }
}

// 메인 클래스에서 인터페이스 활용
public class AbstractApp {
    public static void main(String[] args) {
        Flyer[] ary = new Flyer[2];
        ary[0] = new SuperMan();
        ary[1] = new Bird();

        for (Flyer flyer : ary) {
            flyer.fly();
            // 인터페이스 타입을 통해 Animal의 메서드 호출
            ((Animal) flyer).eating("쌀");
        }
    }
}

추상화의 장점

  1. 유지보수성: 공통 기능은 추상 클래스나 인터페이스로 관리하고, 변경 사항은 해당 클래스나 인터페이스를 수정하면 반영됩니다.
  2. 확장성: 새로운 기능이 필요한 경우, 기존 클래스나 인터페이스를 상속하거나 구현하면 됩니다.
  3. 코드의 일관성: 인터페이스나 추상 클래스를 통해 설계 표준을 명확히 정의할 수 있습니다.
profile
풀스택 개발자로서의 도전을 하는 중입니다. 많은 응원 부탁드립니다!!😁

0개의 댓글