배움 __IL/TIL 1기

TIL : 17번째- 221221 [12-3-수]

Mo_bi!e 2022. 12. 21. 21:12

I. INTRO : 지난시간 복습

 

상속은 두가지가 있다. 

has a 상속 Vs Is a 상속

 

오늘날 책은 주로 Is a 상속이 있으나, has a 상속도 있음

has a 상속은 부품을 들고오는 것인데, 이것도 상속이라고 부른다

우리 문화는 수직관계 상속만 있는데, has a 처럼 수평적 상속에 대해서 낯설다

그렇기 때문에 has a 에 대해서 상속보다는 결합으로 생각하는것이 바람직하다. 

 

부품과 제품간의 결합관계 : has a 관계

왜냐하면 캡슐은 독단존재가 아닌 main함수부터 분가한 것이다.

과거에는 함수간의 분가로 보았으나, 지금은 객체단위로 분가한다.

 

캡슐간 떼기보다는 엮어야하는데 이것이 has-a 관계

이경우 main이 제품이고, 분가한 녀석이 부품이다. 즉 main이 부품을 이용하는것이다.

분해된것을 의존객체라고한다.

 

<상대적인 제품 - 부품 관계>

main 제품 중간부품 (제품) 부품

부품은 상대적인것이다. 중간캡슐은 main에 부품이나, 끝 캡슐에게는 부품이다.

컴퓨터는 제품이나, 로봇에는 컴퓨터가 부품이다.

담는캡슐 연속 각각을 has-a 라고한다.

 

com / assosi는 결합 vs 분리 느낌이다.

 

하나가 아니라 여러개를 가지면 집합관계라고 한다 : aggregation

언제 담는지는 모르지만 필요할때마다 만들어서 담게되면은 aggre (그래서 마름모 투명 ◇)

필요할때마다가 아니라 생성자에서 빈객체(값은 나중에)라도 미리 만들면 composi (그래서 마름모 색칠 ◆)

 

II. 상속

1. has - a 상속 [DI (Dependency Injection) : 부품의 결합 == 의존성주입]

(1) DI

B b = newB();

A a = new A();

a.setB(b); //여기서 b가 부품임 (setB라서)

 

결합을 Injection이라고 한다. 주사기인 이유는 투약하는 느낌이다. (우리문화와 다름)

의존'성' (화학성분 느낌)을 주입하다. 보이는건 물건인데 성격을주입한다. 

뭔가 어렵다 그냥 부품결합한것으로 보자

 

<결국>

부품결합 == 의존성 주입

 

(2) DI의 두가지 방식

1) Setter Injection (for Association)

B b = newB();

A a = new A();

a.setB(b); //여기서 b가 부품임 (setB라서)

 

2) Construction Injection (for Conposition)

오버로드 생성자를 통해서도 결합이 가능하다.

B b= new B();

A a = new A(b);

 

 

2. is - a 상속 : Framework

이제 우리가 말하는 상속을 볼수있는 상속은 is -a 상속이있다.

(1) has - a 상속 vs is - a 상속 으로 만들기

아이언봇 몸통이 없다. 제3의 몸통의 을 가져온것을 is-a 상속이라고한다.

틀을 가져와서 살짝 고치기 (아이언봇부품을 가져올수도있으나 : has -a) 이다.

이런것을 is -a 관계이다.

 

일반로보트가 있다. 이 경우 일반로보트 로 아이언봇 수월하게 만들수 있다.

이러한 틀을 Framework (is -a)라고 한다.

is -a 상속 쓰고, 고쳐쓰는방법을 잘 해보아야...

 

(2) 코드 재사용

is - a 상속 이전에, 코드 재사용이란 배포한 그 자체의 바이너리파일 틀로 재사용하는것이다

만약 소스코드를 바꾼다면 그것은 설계도 자체를 변경한 것이다. 이런경우는 코드재사용으로보기 어렵다.

 

스포츠카를 만들고 싶으면 일반 자동차를 틀로해서 만든다. 같은 자동차이기때문이다

마찬가지로 뉴랙고등학교에 컴퓨터과목 외 다른 성적을 쓰고 싶으면 다 쓰든안쓰든 가저와야한다.

 

(3) Is -a 상속 만들어보기(구현)

기존 구조화된 데이터인 Exam 에다가 Newlec 학교에서 이 구조를 들고오싶다.

이 경우 is - a Vs has -a 인데 

has - a 상속과 is - a 상속을 2가지를 한번에 쓰는것은 바람직 하지 않다.

//방법은 두가지이다.

//첫번째 방법 is -a
public class NewlecExam extends Exam{ 
        //둘사이 상대적으로 부모자식관계(기반,확장클래스 / )
	
//두번째 방법 has -a
//	private Exam exam;
	//두번 쓸수없다
	}
}

 

(4) 부모를 가지는 클래스는 두개의 객체를 생성한다

객체를 만들 때 딱 부모만큼 메모리 만들고 + 확장된 부품(붙이기)으로 만든다.

[[super : Exam] this.NewlecExam

 

이 경우 this는 전체틀만 바라보는것은 super라고한다 (this : 전체 / super : 틀)

부모가 있는 객체를 만들때는 super 가있다.

 

1) 부모의 멤버변수가 private 인데?

public class NewlecExam extends Exam{ 
	
    private int com;

	public NewlecExam() {
		kor = 1;
        }

틀을 가져다 쓸 때 자기것 처럼 써야한다. 그러나 부모클래스의 멤버변수는 private이다

즉 부모가 상속해주더라도 부모것을 함부로 쓸수 없는것이다.

 

2) setter 등으로 이용해볼까?

그래서 setter 등으로 접근이 가능할수도 있다.

public class NewlecExam extends Exam{ 
	
    private int com;

	public NewlecExam() {
		
        setKor1(1); //초기화 내가하는게 맞나?

}

그러나 내가 부모값을 이렇게 자식클래스에서 setter 로 이용해서 초기화하는 것은 바람직 하지않다.

 

3) Overide 이용해보자

public class NewlecExam extends Exam{ 
	
    private int com;

	public NewlecExam() {
		
       //여기서 생성자, 생성자 오버로드 호출할수없나?
		//부모것은 부모가 초기화 내것인 내가 초기화해보자
		
//		this.com = 0; //불가
		//순서가 바뀔수 없다. 따라서 한번 만들어진곳에 또 만들수없다
		
		super();// 따끈한 exam 객체가 만들어진다
        
        this.com = 0; //가능

}

부모는 부모가 초기화 / 나는 내가 초기화 하게끔 하자

 

에 대한 생성자 호출은 this()

부모에 대한 생성자 호출은 super() 

 

다만 이런 경우 생성자 호출은 '순서'가 중요하다

super() => this() 순서로 해야한다.

왜냐하면 super로 먼저 틀을 만들고 그 틀의 메모리 끝에 this로 붙이는 원리이기 때문이다.

 

4) 생성자 Overload를 하자

public class NewlecExam extends Exam{ 
    
    private int com;
    public NewlecExam() {
        this(0,0,0,0);
    }

    public NewlecExam(int kor, int eng ,int math, int com) {

        super(kor, eng, math); //받은것은 여기서 매개변수 넣어서가능
        //이렇게 하면 중복된다 위 기본메소드에대해서 집중화 해주어야		
        this.com = com;
    }

생성자 오버로드를 이용해서 부모 클래스의 값에다가 자식클래스의 멤버변수를 새롭게 붙여서 초기화가 가능하다.

 

 

(5) total() 틀의 고쳐쓰기 : override

1) Override란

우선 자식클래스 필드에 자동완성을 해보면 부모클래스의 메소드 들을 쓸수있다. 이 경우 자동완성에 override라고 적혀있다

여기서 Override란 자식이 고쳐쓸수있다는 의미를 의미한다. (종이위에 투명종이 그리고 그위에 고쳐쓰기)

override는 기존것 위에 타서 덮어 씌어버린다고 이해하는것이 수월하다.

즉 이것은 그 안에 들어가서 수정하는것이 아니라 그 위에서 고쳐쓰는것이다.

 

여기서 overload와의 차이는 overload()는 과적(파라미터를) vs override는 덮어서타기

이다. 

 

2) total() 의 반환 값

이 경우 total()의 반환값은 4점이 아니라 3점이 나온다. total의 틀(super)만있고, this가 추가로 붙여져있기 않기 때문이다.

public class InheritanceTest {
	public static void main(String[] args) {
		
		//이방식은 부품이 아니라 틀로 가져다 쓰는것
		NewlecExam exam =  new NewlecExam(1,1,1,1);
		System.out.println(exam.total());;
		//그러나 이 경우 점수는 4점이 아니라 3점이 나온다.
		//왜냐하면 total 은 틀이 만들었기 때문이다
		
		//틀을 가져다 쓸때 중요한건 고쳐쓸건 고쳐쓰는게 중요하다.
		
		System.out.println(exam.avg());
}

 

public class NewlecExam extends Exam{ 
    
    private int com;
    public NewlecExam() {
        this(0,0,0,0);
    }
    
	@Override
	int total() {
		// TODO Auto-generated method stub
		return super.total() + com; //뒤에 com 을 추가해서 할 수있다.
	}
    }}

이런방식으로 super의 total을 가지고와서 그 위에 덮어서 고쳐쓰기를 한다.

 

3) avg() 의 경우

한편 avg()는 total과 같이 못해서 어려움이 다소 있다.

@Override
	double avg() {
		// TODO Auto-generated method stub
        
//		return super.avg() ; //불가
		//이렇게 못씀 차라리 아래처럼 하는게 바람직
		
		return total() / 4.0;

	}

 

4)결

다른 클래스꺼 로 쓸때 확장하거나, 고쳐쓴다.

보통은 확장할때 관련 메소드 다시 만들어야하지만, 기존 total을 재활용할수있었다

 

★캡슐 결합인데 부품인지 인지로 생각하기

틀은 그냥 쓰면 뭐하러 함 그냥씀 그냥쓰기 녹록치 않아서 고쳐

 

★생성자 틀을 고쳐쓰기 (오버라이드로 고쳐쓴다)

 

3. 객체와 참조 형식의 개수 : 2가지

(1) 객체형식과 참조형식이 다른경우

참조형식과 객체형식이 다름 일반적인 경우에는 불가

부모자식관계는 가능 이 경우 무엇이 정답?

1) Exam exam = new NewlecExam();

2) newlecExam exam = new Exam();

 

2번은 이름에 두개가 없다.

new Exam()으로 객체를 만들었는데, newlecExam exam참조 만족시킬수 있는 객체가 '완성형'으로 있어야한다. 그래서 불가능하다. 부족한 부분이있다.

 

1번이 정답 :  why?

exam 은 이름에 불과 고래와 고래밥

exam 을 부르면 고래를 혹은 고래밥을 호명하는 것일수도 있다. 이름은 두군데 붙일수있다.

new newlecExam()로 인한 객체에 Exam exam참조는 그것의 객체의 Exam 범위만 참조한다. 

 

즉 전체와 일부중 누구를 참조할것인가 관계이다.

이 경우 참조형식은 2개가 될수있다. 비록 하나는 Exam()객체 일부분만으로 되더라도...

 

4. 참조형식과 호출되는 메소드의 관계

(1) 형식에 따른 재정의 함수 호출 관계 : 어떤 함수를 우선적으로 호출할 것인가?

exam1 의 경우 자명하게도 4가 호출된다. override 했기 때문에

exam2 의경우도 4가 호출된다. 비록 부모클래스의 참조변수를 가지고, 그에 따른 함수를 호출하더라도 우선순위는 실제 객체가 생성된 부분의 함수우선적으로 호출된다.

 

(2) 부모클래스에 toatal()이 없는 경우?

부모클래스에 total()이 없다.

exam1은 당연시 자신의 메소드에 가서 호출하면된다.

그러나 exam2는 참조가 가진 메소드에 total()이 없다. 그래서 오류가 난다

(앞에서 본바와 같이 만약 Exam에 total()이있으면 이를 매개하여 자식클래스의 override된 메소드가 호출된다)

 

<정리>

참조형식에 따라서 호출할수 있는 함수가 결정되고, 객체 따라서 오버라이드 한 함수가 있으면 그것이 먼저 호출된다.

 

 

(3) 호출 되는 함수의 위치 결정문제

위의 경우  a.f6()은 불가능하다. 노란색 범위만 호출이 가능하다. (참조 자료형식 때문에)

 

한편 좌측의 경우에는 가능하다.

형변환을 해준다. 우선순위 때문에 괄호를 해주어야한다.

그렇지 않으면 이전상태 그대로 호출되어서 여전히 오류발생

 
 

 

그 외 b.f2() /a.f2()는 큰 차이는 없다 다만 후자의 경우 B의 f6() f7()에 접근하지 못한다. 즉 범위의 차이이다

a.f4() / b.f4() 는 무한루프

 
<정리>

부모가 볼수있는 영역(부모 참조변수)이면 객체가 자식객체 상태이면 자식 것이 우선시된다.

 

 

-이러한 것들이 프레임웍에 중요한이유가 있다.

A가 프레임웍이면 기본적함수를 다 만들어뒀다가, B에서 확장하면서 A것하나씩 오버라이드 하면 된다.
그런데 이경우 흐름에 문제시된다 프레임웍은 흐름을 도둑질 하는 것이다.

 


1. 보충

(1) 참조형식 관련 (뉴렉처 강의 객체지향 18강)

 

(2) DI 관련 (뉴렉처 강의 스프링 3강)

 

2. 회고 

1) has -a 와 is-a의 관계(부품과 틀)

캡슐은 결합인데 그 결합은 부품인지 인지로 대별된다.는점이 흥미롭다.
혹은 수평수직관계 이렇게 상속에 대해서 지도가 완성되어가는 느낌이다. 상속간의 관계를 어떻게 설계해야할지 느낌은 온다

 

2) DI와 그의 두가지 방식

알아보니 스프링의 내용이 나온다. 어느정도 이해하고, 추후에 스프링에서 어떻게 조우할지 기대가 된다.

 

3) is-a 의 override가 framework

그렇게 주구장창 들었던 framework 가 이런것이고 이렇게 이용한다는 점이 흥미롭다.
그래서 주변에서 말했던 오히려 framework에 오면 코드가 많이 줄어든다고 하는것일까?
즉 중간에 메인흐름은 둔채 흐름 몇개만 가지고 놀기 때문이 아닐까?

 

4) 참조변수와 객체의 자료형이 다른경우

이렇게 다른경우에 대해서 생각하지 못했는데 완성형인지 여부에 따라서 그것이 달리 될 수있다는 것이 흥미롭다.

한편 참조변수에 대해서 결코 공간이라 말하면안된다.

 

5) 4)의 경우에 호출되는 메소드는 무엇인가?

호출되는 메소드는 참조변수의 메소드보다 객체의 메소드까지 우선시 접근한다는 것이 흥미로웠다.

'배움 __IL > TIL 1기' 카테고리의 다른 글

TIL : 19번째- 221223 [12-4-금]  (0) 2022.12.23
TIL : 18번째- 221222 [12-4-목]  (1) 2022.12.22
TIL : 16번째- 221220 [12-3-화]  (0) 2022.12.20
TIL : 15번째- 221219 [12-3-월]  (1) 2022.12.19
TIL : 14번째- 221216 [12-2-금]  (2) 2022.12.16