객체 지향 프로그래밍 설계 하기
상속
수평적 구조와 수직적 구조의 설계 방법
- 수평적 구조로 설계 한다면? → 각 클래스마다 DTO, VO를 만들어야한다.
- 객체를 설계하다 보면 비슷한 클래스의 경우 중복적인 요소가 발생한다.
- 객체를 수평적인 구조로 설계하면 단점
- 코드의 중복이 발생
- 새로운 요구사항 발생시 반영이 어렵다(유지보수가 어렵다)
- 확장성이 떨어진다.
- 수직적 구조로 설계한다면?
- 수직적 구조 = 계층화 = 상속 = 클래스와 클래스의 관계 설계
- 부모가 확장(상속)(extends) 시켜서 자식이 부모의 정보를 사용가능 하기 때문에 코드의 중복부분을 최소화 할 수 있다.
- 새로운 요구사항 발생 시 부모 클래스만 수정하면 되기 때문에 유지 보수가 쉽다.
- 클래스를 계층화 해서 사용할 때의 장점
- 클래스를 계층화 하는 것을 상속이라고 한다.
- 코드의 중복을 최소화 할 수 있다.
- 새로운 요구사항 발생시 반영이 쉽다(유지 보수가 쉽다.) → 부모 클래스만 수정하면되기 때문에
- 확장성이 좋아진다. → 코드가 복잡해지긴한다.메모리를 통한 상속의 이해
- 상태정보 재활용
- 상속은 UML(Unified Modeling Language)를 사용하여 표현
- super class : 상위 클래스, 부모 클래스
- 일반화, 추상화, 개념화, 포괄적
- sub class : 하위 클래스, 자식 클래스, 파생 클래스
- 구체화(구상화), 세분화
- protected : 상속관계에서 하위 클래스가 상위 클래스의 접근을 허용하는 접근 권한
- 같은 패키지 내에 있을 때만 사용 가능
- 자식 클래스에서 protected 선언한 정보에 접근 가능하기 때문에 정보은닉의 위배된다는 위험이 존재
- 정보은닉을 위해 private을 사용하면 자식 클래스에서 접근이 불가능 하므로 protected 사용 → 자식은 protected로 선언한 객체에만 접근이 가능하다.
- super() : 상위 클래스의 생성자를 호출\
- 상속 관계에서 객체 생성
- 클래스명 객체 변수 = new 클래스명();
- 상속을 사용하면 하위 클래스가 상위클래스를 재활용 가능하다.
- 하위 클래스가 상위클래스르 접근하여 사용 가능
- 자식이 부모의 상태정보를 마음대로 접근하는 것은 정보 은닉에 위배된다.
- 정보 은닉을 적용하기 위해서 자식 클래스에서 객체를 생성해서 직접 데이터를 부모 클래스로 보낸다.
- 부모의 생성자를 호출해서 부모 클래스에서 직접 데이터를 초기화 하는 방식이 바람직하다.
- 부모 클래스의 상태정보를 protected가 아닌 private로 설정하고, 자식 클래스에서 직접 데이터를 보내서 부모 클래스에서 초기화 하는 방식으로 정보 은닉을 구현
// 부모
public class Employee{ // object 클래스 -> 최상위 클래스 ( extends Object)
private String name;
private int age;
private String phone;
private String empDate;
private String dept;
private boolean marriage;
public Employee(){
super(); // 상위 클래스의 생성자를 호출 -> new Object()에 의해서 객체가 생성된다.
}
public Employee(String name, int age, String phone, String empDate, String dept, boolean marriage) {
this.name = name;
this.age = age;
this.phone = phone;
this.empDate = empDate;
this.dept = dept;
this.marriage = marriage;
}
// 자식
public class RempVO extends Employee {
public RempVO(){
super();
}
public RempVO(String name, int age, String phone, String empDate, String dept, boolean marriage){
// 초기화(자식이 부모의 기억공간에 초기화를 하는 경우)
super(name,age,phone,empDate,dept,marriage); // 부모의 생성자를 호출
}
// main
public class EmployeeTest {
public static void main(String[] args) {
// [일반사원] 한명의 객체를 생성하고 데이터를 저장후 출력을 하세요.
RempVO vo = new RempVO("홍길동",35,"010-1111-1111","2022-11-10","기획부",false); // 객체 생성, Employee 상속
System.out.println(vo.toString());
}
}
상속관계에서 객체 생성 및 Override
동작 측면에서 클래스 설계
- 수평적 구조 측면
- 중복이 발생한다.
public class DogCatTest {
public static void main(String[] args) {
// Dog [객체를 생성]하고 eat()동작을 구동해보자.
Dog d = new Dog();
d.eat();
// Cat [객체를 생성]하고 eat(), night() 동작을 구동해보자.
Cat c = new Cat();
c.eat();
c.night();
}
}
public class Dog {
public void eat(){
System.out.println("개처럼 먹다");
}
}
public class Cat {
public void eat(){
System.out.println("고양이처럼 먹다");
}
public void night(){
System.out.println("밤에 눈에서 빛이 난다.");
}
}
- 수직적 구조 측면
- 중복적인 구조를 부모 클래스로 빼서 상속을 하여 사용하여 확장
public class Dog extends Animal{
}
public class Cat extends Animal{
public void night(){
System.out.println("밤에 눈에서 빛이 난다.");
}
}
- 상속 구조를 왜 사용해야 할까?
- 클래스 안에 있는 내용을 숨긴다(소스코드를 주면 안되기 때문에)
- 중간 인터페이스 역할을 할 수 있는 클래스(부모 클래스)를 통해서 자식 클래스의 역할을 알 수 있고 사용 할 수 있게 설계해야 하고 이를 상속으로 할 수 있다.
상속관계에서 객체 생성 방법
- Upcasting
- 부모가 자식을 가리키는 객체 생성 방법
- 부모 클래스를 통해서 자식 클래스를 사용하는 방법
- 작은 타입이 큰 타입으로 들어간다.
- Upcasting : Dog의 소스코드를 주지 않고 Dog의 클래스와 상속 관계인 것만 알면 Animal 부모 클래스로 Dog 자식 클래스를 사용하기 위해서 사용
public class DogCatUpcastingTest {
public static void main(String[] args) {
// Dog객체를 사용해보자
// Dog d=new Dog();
// d.eat();
// Upcasting(업케스팅)
// 부모가 자식을 가리킨다.
Animal ani = new Dog();
ani.eat(); // 동물처럼먹다 -> 개처럼 먹다
ani = new Cat();
ani.eat(); // 동물처럼먹다 -> 고양이처럼 먹다.
}
}
상속 체이닝과 super
- 상속체이닝
- 맨 위 부모클래스 부터 객체가 생성되어 자식까지 연결되는 구조
- super()
- 상위 클래스의 생성자를 호출하는 메서드
- 생성자 메서드에서 가장 첫 문장에 사용해야한다. ( First Statement)
- 상위 클래스 객체를 먼저 생성하고 수행문을 수행해야한다.
- 상위 클래스의 기본생성자를 호출하는 super()는 생략되어있다.
메서드의 재정의(Override)가 필요한 이유
- Override(재정의)
- 상속관계에서 하위클래스가 상위 클래스의 동작을 재정의 하는 행위(기능 추가, 변경)
- 동적바인딩
- 실행시점에서 사용될 메서드가 결정되는 바인딩
- 컴파일 시점
- 부모의 시점에서 실행
- 실행 시점
- 자식의 시점에서 실행
- 컴파일 시점
- 실행시점에서 사용될 메서드가 결정되는 바인딩
public class OverrideTest {
public static void main(String[] args) {
// Upcasting : Dog의 소스코드를 주지 않고 Dog의 클래스와 상속 관계인 것만 알면 Animal 부모 클래스로 Dog 자식 클래스를 사용하기 위해서 사용
Animal ani = new Dog(); // 부모 클래스로 Dog 자식 클래스 사용
ani.eat(); // Animal---(동적바인딩)-->Dog
ani=new Cat(); // 같은 부모 클래스를 사용하기 때문에 Animal 선언 x
ani.eat();// Animal---(동적바인딩)-->Cat
}
}
// 부모가 가진 eat를 재정의(Override)
public void eat(){
System.out.println("개처럼먹다");
}
// 부모가 가진 eat를 재정의(Override)
public void eat(){
System.out.println("고양이처럼 먹다");
}
'개발 > Java' 카테고리의 다른 글
다형성 (0) | 2024.01.29 |
---|---|
객체 형변환 (0) | 2024.01.28 |
객체 생성과 static 과의 관계 (0) | 2024.01.28 |
JVM이 사용하는 메모리 영역 (0) | 2024.01.28 |
Static (0) | 2024.01.28 |