JAVA - Newlecture (3)

suesue lee·2022년 3월 3일
0

JAVA

목록 보기
4/6

이 글은 Newlecture의 객체지향 프로그래밍 강의를 보며 정리한 글입니다.

🤍 객체지향 프로그램 (OOP-Object Oriented Programming) 🤍

OOP의 기본컨셉은 프로그램 내에서 표현하고자 하는 실 세계 (real world)의 일들을 객체를 사용해서 모델링하고, 객체를 사용하지 않으면 불가능 혹은 무지 어려웠을 일들을 쉽게 처리하는 방법을 제공한다는 것입니다.

객체는 당신이 모델링하고자 하는 일이나 기능 혹은 필요한 행동들을 표현하는 프로그램 코드와 그와 관련된 데이터로 구성됩니다. 객체는 데이터 (그리고 함수 역시)를 감싸서 (공식적인 표현 : encapsulate) 객체 패키지 (해당 객체를 참조하기 위한 이름. namesapce라고도 불린다.)
안에 보관한다. 이는 계층 구조를 만드는데 용이하고 사용하기에도 쉽게 하기 위해서다.

또한, 객체는 네트워크를 통해 쉽게 전송될 수 있도록 데이터를 저장하는 용도로도 많이 사용된다.

🤍 객체지향 프로그래밍의 시작 캡슐화 🤍

⭐ 함수는 외부의 수정에 절대 영향을 받아서는 안된다. ⭐
함수 모듈의 독립성은 구조화된 데이터를 사용하는 함수 모듈의 독립성을 침해하는 문제를 해결 할 수 있기 때문이다.

코드 파일 관리방법 :
구조화 된 객체를 사용하는 함수는 객체의 구조 변경에 아주 취약하기에 캡슐화를 통해 데이터 구조에 따른 코드의 수정범위를 캡슐 범위로 한정할 수 있다.

캡슐화 정리 : 데이터 구조와 함수를 하나의 영역에 함께 정의하는 것이다.

🤍 함수들을 캡슐화 하기 🤍
List Program → Program 이름변경
ExamList.java에 Program안에 있는 (ExamList) 코드들을 이동시킨다.

(ExamList.java)
main 함수를 제외한 
public class ExamList {
	Exam[] exams;
    int current;
static void printList(ExamList list) {
...
}
static void printList(ExamList list, int size) {
...
}
static void inputList(ExamList list) {
...
}
public static void init (ExamList list) {
    list.exams = new Exam[3];
    list.current = 0;
}
(Program.java)
package part3.ex.1.캡슐화;

import java.util.Scanner;

public class Program {
	public static void main (String[] args {
    
    ExamList list = new Examlist();
    ExamList.init(list);
    
    int menu;
    boolean keepLoop = true;

    while(keepLoop)
    {
        menu = inputMenu();

        switch(menu) {
        case 1: 
            ExamList.inputList(list);
            break
        case 2:
            ExamList.printList(list);
            break;
        case 3:
            System.out.println("bye");

            keepLoop = false;
            break;

           default;
         }

🤍 인스턴스(Instance) 메소드 🤍

객체와 함수의 관계


list를 이용한 처리 → 주체를 먼저 앞으로 나열하여 다른 함수(입력(), 출력(), 저장())들을 처리



표현식을 편하게 하는 것과 더불어 list에게 역할을 명령한다.

🤍 인스턴스(Instance) 메소드 구현하기 🤍
기존의 함수의 인스턴스 전달

public static void main (String[] args){
	ExamList list = new ExamList();
    **ExamList.inputList(list)** // funtion (함수), static method(고전적인 메소드)라고도 부른다.
    }

class ExamList 
{
	public Static void inputList(ExamList list)
    {
    	list.exams[list.current] = new Exam();
      	}


새로운 함수의 인스턴스 전달

public static void main (String[] args){
	ExamList list = new ExamList();
    **list.inputList();** // instance method (메소드), 객체를 통해서만 호출됨
    }

class ExamList 
{
	public void inputList()
    {
    	this.exams[this.current] = new Exam();
    }

⭐ 기존함수와 새로운 함수의 차이점 ⭐
기존함수 : static(키워드) / ExamList list (파라미터) 를 넘겨 받는데에 차이가 있다.
새로운 함수 : this 라는 키워드로 묵시적으로 넘겨받을 수 있으며, static이라는 키워드를 빼면, instance method 가 된다.


Program.java

pacakage part3.ex2.메소드

import java.util.Scanner;

public class Program {
	public static void main(String[] args) {
    
    	ExamList list = new ExamList(); 
        list.init();
        
        int menu;
        boolean keepLoop = true;
        
        while(keepLoop)
        {
          menu = inputMenu();

          switch(menu) {
          case 1:
           // ExamList.inputList(list);
              list.inputList();
              break;
          case 2:
           // ExamList.printList(list);
              list.printList();
              break;
          case 3:
              System.out.println("bye");

              keepLoop = false;
              break;

          default:
              System.out.println("잘못된 값을 입력하셨습니다.");
              
        }

ExamList.java

package part3.ex2.메소드;

import java.util.Scanner;

public class ExamList {
	Exam[] exams;
    int current;
    
    void printList() {
    	printList(current);
      }
      
    void printList (int size) {
    	System.out.println("성적출력")
        System.out.println();
        
        // int size = list.current;
        Exam[] exams = this.exams;
        
        for(int i=0; i<sizel i++) {
        	Exam exam = list.exams[i];
            int kor = exam.kor;
            int eng = exam.eng;
            int math = exam.math;
            
            int total = kor+eng+math;
            float avg = total/3.0f;

             
 /*  ** static void inputList() ** {
   
      Scanner scan = new Scanner (System.in);
      System.out.println("성적입력");
      System.out.println();

          int kor, eng, math 

          do{
            System.out.printf("국어 : ");
            kor = scan.nextInt();

        if(kor < 0 || 100 < kor)
            System.out.println("국어성적은 0~100 까지입니다.");

        } while (kor < 0 || 100 < kor);

          do{
            System.out.printf("영어 : ");
            eng = scan.nextInt();

        if(eng < 0 || 100 < eng)
            System.out.println("영어성적은 0~100 까지입니다.");

        } while (eng < 0 || 100 < eng);

           do{
            System.out.printf("국어 : ");
            math = scan.nextInt();

        if(math < 0 || 100 < math)
            System.out.println("수학성적은 0~100 까지입니다.");

        } while (math < 0 || 100 < math);

          Exam exam = new Exam();
          exam.kor = kor;
          exam.eng = eng;
          exam.math = math;

          Exam[] exams = **this.exams;**
          int size = **this.current;**

        if(exam.length == size) {
        // 1. 크기가 5개 정도 더 큰 새로운 배열을 생성하시오.
        Exam[] temp = new Exam [size + 5];
        // 2. 값을 이주시키기
        for (int i=0; i<size; i++)
        temp[i] = exams[i];
        // 3. list.exams가 새로만든 temp 배열을 참조하시오.
        **this.exams** = temp;

        }

        **this.exams[this.current] = exam;**
        **this.current++;**
        
        */

        }


    static void inputList(ExamList list) {
      Scanner scan = new Scanner (System.in);
      System.out.println("성적입력");
      System.out.println();

          int kor, eng, math 

          do{
            System.out.printf("국어 : ");
            kor = scan.nextInt();

        if(kor < 0 || 100 < kor)
            System.out.println("국어성적은 0~100 까지입니다.");

        } while (kor < 0 || 100 < kor);

          do{
            System.out.printf("영어 : ");
            eng = scan.nextInt();

        if(eng < 0 || 100 < eng)
            System.out.println("영어성적은 0~100 까지입니다.");

        } while (eng < 0 || 100 < eng);

           do{
            System.out.printf("국어 : ");
            math = scan.nextInt();

        if(math < 0 || 100 < math)
            System.out.println("수학성적은 0~100 까지입니다.");

        } while (math < 0 || 100 < math);

          Exam exam = new Exam();
          exam.kor = kor;
          exam.eng = eng;
          exam.math = math;

          Exam[] exams = this.exams;
          int size = this.current;

        if(exam.length == size) {
        // 1. 크기가 5개 정도 더 큰 새로운 배열을 생성하시오.
        Exam[] temp = new Exam [size + 5];
        // 2. 값을 이주시키기
        for (int i=0; i<size; i++)
        temp[i] = exams[i];
        // 3. list.exams가 새로만든 temp 배열을 참조하시오.
        exams = temp;

        }

        exams[current] = exam;
        current++;

        }

      public void init() {
          exams = new Exam[3]; // this는 생략가능하다. 
          current = 0;
          
        }

    

🤍 캡슐화의 은닉성 🤍

캡슐을 깨지 못한게 하는 도구 : 접근 제어 제시자

접근지시자동일 클래스파생 클래스외부 클래스
privateOXX
protectedOOX
publicOXO

Program.java

pacakage part3.ex2.메소드

import java.util.Scanner;

public class Program {
	public static void main(String[] args) {
    
    	ExamList list = new ExamList(); 
        list.init();
        **list.current = 3;**
...
	}

ExamList.java

package part3.ex2.메소드;

import java.util.Scanner;

public class ExamList {
	private Exam[] exams;
    **private** int current;
    public void printList() {
    	printList(current);
      }
      
public void printList (int size) {
    System.out.println("성적출력")
    System.out.println();
...
	}
    
publicvoid inputList(ExamList list) {
    Scanner scan = new Scanner (System.in);
    System.out.println("성적입력");
    System.out.println(); 
...
	}

🤍 생성자 (Constructor) 🤍
: 초기화를 위한 특별한 함수

생성자의 조건
1. 객체가 생성 되자 마자 무조건 제일 먼저 실행되어야만 한다.
2. 생성될 때 단 한 번만 실행되어야만 한다.

new ExamList();

new ExamList ➕ (); ⇒ 생성자는 함수명이 없다.

class ExamList{

	public ExamList(){
    	exams = new Exam[3];
        current = 0;
      }
}

⇒ 정의를 할 때의 함수명은 초기화 할 객체를 한정하기 위한 형식명칭이다.


ExamList.java

public void init() {
	exams = new Exam[3]
    current= 0;

public ExamList() {
	exams = new Exam[3]
    current= 0;

Program.java

list.init(); → 삭제 

🤍 생성자 오버로드 (Constructor Overload) 🤍

class ExamList {
	private Exam[] exams
    private int current;
    
    * public ExamList() {
    	exams = new Exam[3];
        current = 0;
	}

	public ExamList(int size) {
    	exams = new Exam[size];
        current = 0;
	} *
}

 new ExamList();
 new ExamList(10);

기본생성자를 제거하는 경우

new ExamList();

를 사용할 수 없다.

생성자의 중복 제거



코드가 중복 되는 것을 방지하고,

public ExamList(int size) {
	exams = new Exam[size];
    current = 0;
  }

위와 같이 한 쪽으로 몰아서 사용한다.

생성자를 하나도 정의하지 않는다면 ?


생성자가 없는 경우, 기본생성자는 Compiler가 생성해준다.

( 객체의 생성에 있어 생성자는 반드시 필요하다.
이 때, overload 한 것이라도 있다면 객체를 생성할 수 있는 문법구조를 갖출 수 있기 때문에 기본생성자를 Compiler가 생성해준다. )
( Compiler는 생성자가 하나도 없을 때만 관여해서 만들어줄 뿐, 그렇지 않다면 생성자를 만들어 주지 않는다. )

(Overload)

public ExamList(int size) {
	exam = new Exam[size]
    current = 0;
}

위와같이 overload 만 되어 있을 경우
new ExamList(); 와 같은 객체생성이 불가능하다.

private Exam[] exams
private int current;


참조변수가 실체화 되는 경우
참조변수는 null
각변수는 0 이 된다.

🤍 Getter와 Setter 그리고 이것을 써야하는 이유 🤍

ExamList.java

package part3.ex3.Getters와 Setters;

import java.util.Scanner;

public class ExamList{
	private Exam[] exams;
    private int current;
    
	public void printList() {
    	printList(current);
	}
    
 public void printList(int size) {
 	System.out.println("성적 출력");
    System.out.println();
    
    // int size = list.current;
    Exam[] exams = this.exams;
    
    for(int i=0; i<size; i++) {
    Exam exam = exams[i];
    int kor = exam.getKor(); //exam.kor;
    int eng = exam.getEng(); //exam.eng;
    int math = exam.getMath(); //exam.math;
    
    int total = kor+eng+math;
    float avg = total/3.0f;
    
    System.out.printf("국어 : %d\n". kor);
    System.out.printf("영어 : %d\n". eng);
    System.out.printf("수학 : %d\n". math);

    System.out.printf("총점 : %3d\n". total);
    System.out.printf("평균 : %6.2f\n". avg);
    System.out.println("----");
    }
    ...
   }while(math < 0 || 100 <math);
   
   Exam.setKor(kor) //exam.kor = kor;
   Exam.setEng(eng) //exam.eng = eng;
   Exam.setMath(math) //exam.math = math;
  }
}   

Exam.java

package part3.ex3.Getters와 Setters;

public class Exam {
	
	int kor1;
    int eng;
    int math;
    
    public int getKor() {
    return kor;
  }
    public int getEng() {
    return eng;
  }
    public int getMath() {
    return math;  
  }
 	public void setKor(int kor){
    	this.kor1 = kor;
    }
    public void setEng(int eng){
    	this.eng = eng;
    }
    public void setMath(int math){
    	this.math = math;
    }
}

구조가 변경된다는 말의 의미는?


Getters/Setters 를 사용한다면 내부적으로만 바뀌고, 변화가 외부에서는 감지가 되지 않는다. (외부에서 문제 ❌)

🤍 Exam 캡슐 완성하기 🤍

Exam.List

package part3.ex3.Getters와Setters

	for(int i=0;i<size; i++) {
      Exam exam = exams[i];
      int kor = exam.getKor();
      int eng = exam.getEng();
      int math = exam.getMath();

      int total = exam.total(); //kor+eng+math;
      float avg = exam.avg(); //total/3.0f;

...

	}while(math < 0 || 100  < math);
   
   /*
    Exam exam = new Exam();
    exam.setKor(kor);
    exam.setEng(eng);
    exam.setMath(math);
    */
    
    Exam exam1 = new Exam(); // 오류가 나는 경우 기본생성자도 추가해주어야 한다. 
    
    Exam exam = new Exam(kor, eng, math);
    
    Exam[] exams = this.exams;
    int size = this.current;
    
...

}

Exam.java

package part3.ex3.Getters와Setters

public class Exam {
	int kor;
    int eng;
    int math;
   
 public Exam(int kor, int eng, int math) {
 	this.kor = kor;
    this.eng = eng;
    this.math = math;
    // 변수명이 같은 경우 식별가능하도록 this로 구분을 해준다. 
 
 (Overload)
 public Exam() {
 	thist(0,0,0);
 }

 ❕ 마우스 우클릭 → 'Source' → 'Generate Getters and Setters' 클릭 ❕ 
   
   ...
 public int total() {
 	return kor+eng+math;;
    }
 public float avg() {
 	return total/3.0f; //this.total에서 this는 생략가능.
 	}
 }


getKor() / getEng() / getMath() = get관련 함수 Getters

setKor() / setEng() / setMath() = set관련 함수 Setters

🤍 UI 코드는 분리하는 것이 기본 🤍

↓ 추가하는 기능을 분리하고, (Console) input()/print() , (ExamList) add()/get() 을 따로 뺀다.

↓ 결과적으로는 이렇게 만들어진다.


ExamConsole.java 추가

package part3.ex4.UI코드분리하기;

public class ExamConsole {

}

ExamList.java

public voiud printList(int size){
	System.out.println("성적 출력");
    System.out.println();
     
    for(int i=0;i<size;i++) {
    	Exam exam = this.get(i); //this.exams[i];
        int kor = exam.getKor();
        int eng = exam.getEng();
        int math  = exam.getMath();
        
        int total = exam.total();
        float avg = exam.avg();
        
        ...
        
        System.out.printf("총점 : %3d\n", total);
        System.out.printf("평균 : %6.2f\n", avg);
        System.our.println("----");
    }
}
private Exam get(int i) {
	return this.exams[i];
}

public void inputList(){
	Scanner scan = new Scanner(System.in);
    System.out.println("성적 입력");
    System.out.println();
    
    int kor, eng, math;
    
    do {
    
    }while {
    
    }
    
/* -add(데이터 추가)----------------------- */
add(exam);

}

private void add(Exam exam) {

  Exam[] exams = this.exams;
  int size = this.current;

  if(exams.length == size) {
      Exam[] temp = new Exam[size + 5];
      for(int i=0; i<size; i++)
          temp[i] = exams[i];
      exams = temp;
  }
      exams[current] = exam;
      current++;	
}

public ExamList() {
...
}

→ '사용자'로부터 입력 받은 로직과 추가 하는 로직을 분리함.
→ '사용자'에게 출력 하는 로직과 데이터를 꺼내는 로직을 분리함.

🤍 ExamConsole 클래스 구현하기 🤍

ExamList.java

package part3.ex4.UI코드분리하기;

public class ExamList {
	private Exam[] exams;
    private int current;
    
    public Exam get(int i) {
    ...
    }
    public void add(Exam exam) {
    ...
    }
    public ExamList() {
    
    public int size() {
    	return current;
    }
   }

ExamConsole.java

package part3.ex4.UI코드분리하기;

import java.util.Scanner;

public class ExamConsole {

	private ExamList list = new ExamList();
    
	public void printList() {
    	printList(list.size());
    }
  public void printList(int size) {
  	System.out.println("성적 출력");
    System.our.println();
    
    for (int i=0; i<size; i++) {
      Exam exam = list.get(i);
      int kor = exam.getKor();
      int eng = exam.getEng();
      int math = exam.getMath();
      
      System.out.printf("국어 : %d\n", kor);
      System.out.printf("영어 : %d\n", eng);
      System.out.printf("수학 : %d\n", math);
      
      System.out.printf("총점 : %3d\n", total);
      System.out.printf("평균 : %6.2f\n", avg);
      System.out.println("----";
    }
  }
  public void inputList() {
  	Scanner scan = new Scanner(System.out.println);
    System.out.println("성적 입력");
    System.out.println();
    
    int kor, eng, math;
    do {
    	System.out.printf("국어 : ");
        kor = scan.nextInt();
        
        if (kor < 0  || 100 < kor)
        	System.out.println("국어성적은 1~100까지 입니다.");
  } while (kor<0 || 100<kor);
  
    do {
    	System.out.printf("영어 : ");
        eng = scan.nextInt();
        
        if (eng < 0  || 100 < eng)
        	System.out.println("영어성적은 1~100까지 입니다.");
  } while (eng<0 || 100<eng);  
  
    do {
    	System.out.printf("수학  : ");
        math = scan.nextInt();
        
        if (math < 0  || 100 < math)
        	System.out.println("수학성적은 1~100까지 입니다.");
  } while (math<0 || 100<math);  
  
	Exam exam = new Exam (kor, eng, math);  

/* ---add---- */

    list.add(exam);

Program.java

package part3.ex4.UI코드분리하기;

import java.util.Scanner;

public class Program {
	public static void main (String[] args) {
    	ExamConsole list = new ExamConsole();
        
        int menu;
        boolean keepLoop = true;
        
        while(keepLoop)
        {
         menu = inputMenu();
         
         switch(menu) {
          case 1:
          	list.inputList();
            break;
          case 2:
          	list.printList();
            break;
          case 3:
          	System.out.println("bye");
            
            keepLoop = false;
            break;
          default:
          	System.out.println("잘못된 값을 입력하셨습니다. 메뉴는 1~3까지 입니다.");
         }
         ...
        }
    }
}

🤍 Has A 상속 🤍

Has A : 캡슐이 다른 캡슐의 객체를 가지고 있는 상태를 의미한다.

  • 캡슐들은 서로를 사용하거나 사용되는 관계를 가지고 있다.

캡슐의 구조는 A(Program)캡슐이 B(ExamConsole)캡슐을 이용하고, B캡슐이 C(ExamList)캡슐을 이용하고, C캡슐이 D(Exam)캡슐을 이용하는 이용관계에 있는 구조이다.
즉, 캡슐들은 서로를 사용하거나 사용되는 관계를 가지고 있다.


실제 캡슐의 사용관계이다.
Exam 은 ExamList에 포함되어있고, ExamList는 ExamConsole에 포함되어있다.


ExamList를 생략하여 ExamConsole과 Exam을 사용하는 관계가 (구성하는 관계) 일치한다.
Has A 관계라고 한다. = Has A 상속을 의미한다.


Composition Has A : 집합과 관계없이 객체를 ExamConsole이 만들어질 때 구성되는 것들을 같이(한 번에) 만들 것이냐는 의미.
Aggregation Has A : 집합적으로 갖는다. (ExamConsole를 만들 때, Exam 이 없을 수도 있다.)


현재 Aggregation Has A 상속을 이용하고 있다.


dependency : (의존객체) 함수 내에서 사용하는 객체를 말한다.

🤍 코드 재사용이란? 🤍


재사용 할 때에는 Source Code가 아닌 Binary(Class File) 이다.

코드 배포 순서
1. Compile → Exam.class
2. 압축 → Exam.zip
3. jar → Exam.jar

or

배포할 프로젝트 선택후 우클릭 → 'Export' 클릭 → 'JAVA' → 'JAR file' 클릭
사용할 프로젝트 선택후 우클릭 → 'Build Path' 클릭 → 'Configure Build Path'클릭 → 탭에서 'Libraries' 클릭 → 'Add External JARs..' 클릭 → 아까 배포했던 .jar파일 선택 → 'Apply and Close' 클릭

🤍 IS A 상속이란? 🤍

IS A 상속 : 객체지향을 지원하는 플랫폼이다.

객체지향의 3대 덕목 : 캡슐화, 상속, 다형성


frame(틀, 부품)만을 사용할 때에는 IS A 상속을 사용한다.


IS A 상속은 내가 원하는 것에 근접한 것을 IS A 상속 할 수록 생산성은 높아지고, 소요비용도 적어진다.


틀을 'Framework' 이라고 말한다.
Framework의 장점 : 생산성이 좋음
Framework의 단점 : 기성 Framework이기 때문에 중복된다.

🤍 Exam을 IS A 상속하기 🤍


↓ is a 상속에선 기존 코드는 손댈 수 없기 때문에 NewlecExam에 'com:int'를 추가한다.

Program.java

import part3.ex4.UI코드분리하기.Exam;

public class Program {
	public static void main(String[] args) {
    	NewlecExam exam = new NewlecExam();
        exam.setEng(10); // is a 상속 
        
        System.out.println(exam.total());
    
    }
}

NewlecExam.java

public class NewlecExam extends Exam {
	private Exam exam; // has a 상속
}


두 클래스의 명칭 '부모 클래스' / '자식 클래스'

NewlecExam.java

import part3.ex4.UI코드분리하기.Exam;

public class NewlecExam extends Exam {
	private int com;
    
    public int getCom() {
    	return com;
    }
    public int setCom(int com) {
    	this.com = com;
    }
}

Program.java

import part3.ex4.UI코드분리하기.Exam;

public class Program {
	public static void main(String[] args) {
    	NewlecExam exam = new NewlecExam();
        exam.setKor(10);
        exam.setEng(10); // is a 상속 
		exam.setMath(10);
        exam.setCom(10);
        
        System.out.println(exam.total());
	→ is a 상속을 이용해 재사용하는 경우 frame으로 사용할 때 문제점이 몇가지가 있다.     
    }
}

🤍 Oerride(우선순위가 높은) 상속 🤍


자식객체는 'this'를 사용한다. 부모객체는 'super'를 사용한다.
우측에 나와있는 total의 차이점은 NewlecExam에서 사용하는 total은 NewlecExam으로 정의되어있고, Exam에서 사용하는 total은 Exam으로 정의되어있다. (캡슐이 다르다.)
우선순위로 NewlecExam.total()을 찾아보다 없다면, Exam.total()을 사용한다.

NewlecExam.total() {
	return this.kor + this.eng + this.math + this.math
}

를 Override하게 된다면, Exam.total()을 사용하지 않게된다. (우선순위가 NewlecExam.total()이 높기 때문이다.)

수정해야할 코드를 재정의 (Override)

NewlecExam.java

import part3.ex4.UI코드분리하기.Exam;

public class NewlecExam extends Exam {

	private int com;
    
    public int getCom() {
    	return com;
    }
    public int setCom(int com) {
    	this.com = com;
    }
    @Override
    public int total() {
    	// ctrl + space = total() : int 클릭하면 자동으로 Override해준다. 
        return super.total()+com; // 부모가 가지고 있는 total을 사용한다.
    }
    @Override
    public float avg() {
    	return total()/4.0f();
    }
}

Program.java

import part3.ex4.UI코드분리하기.Exam;

public class Program {

	public static void main(String[] args) {
    	NewlecExam exam = new NewlecExam();
        exam.setKor(10);
        exam.setEng(10); // is a 상속 
		exam.setMath(10);
        exam.setCom(10);
        
        System.out.println(exam.total());
        System.out.println(exam.avg());
    }
}

🤍 자식 클래스의 객체 초기화 🤍

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

Program.java

import part3.ex4.UI코드분리하기.Exam;

public class Program {

	public static void main(String[] args) {
    	NewlecExam exam = new NewlecExam(1,1,1,1);
        exam.setKor(10);
        exam.setEng(10); // is a 상속 
		exam.setMath(10);
        exam.setCom(10);
        
        System.out.println(exam.total());
        System.out.println(exam.avg());
    }
}

NewlecExam.java

import part3.ex4.UI코드분리하기.Exam;

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) {
    	
        //this.setKor(kor);
        super(kor, eng, math); // 부모의 생성자가 호출됨.
        this.com = com;
    }    
    public int getCom() {
    	return com;
    }
    public int setCom(int com) {
    	this.com = com;
    }
    @Override
    public int total() {
    	// ctrl + space = total() : int 클릭하면 자동으로 Override해준다. 
        return super.total()+com; // 부모가 가지고 있는 total을 사용한다.
        // 부모객체는 부모의 기능으로 초기화된다. 
    }
    @Override
    public float avg() {
    	return total()/4.0f();
    }
}

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


참조형식은 2개다.
1. NewlecExam exam = new NewlecExam();
2. Exam exam = new NewlecExam();


참조형식이 Exam이라 하더라도 객체가 NewlecExam 이라면 우선순위(Override)라서 NewlecExam이 호출된다.


Exam exam2을 자료형으로 봤을 때, total 함수가 없기 때문에 Exam exam2 형식으로는 오류가 난다.
참조형식이 가지고 있는 메서드에 한에서 Exam exam2 형식을 호출할 수 있다.

⭐ 참조형식에 따라서 호출할 수 있는 함수가 결정된다. Exam exam2 = new NewlecExam(1,1,1,1) 에 따라서 override가 된게 있다면 override 된 함수가 먼저 호출되는 우선순위를 갖는다. ⭐

🤍 IS A 상속 쉬어가기 🤍
GamePrj 생성

Program.java

import java.awt.Frame
public class Program {
	public static void main(String[] args) {
    	GameFrame frame = new GameFrame();
        frame.setVisible(true); 
        //  위의 프로그램은 has a 상속으로 만드는 것이 가능하다. 
    }
}

GameFrame.java

import java.awt.Frame;

public class GameFrame extends Frame {
	@Override
    public void paint(Graphics g) {
    	super.paint(g);
        
        g.drawRect (100, 100, 200, 100);
    }
 }

🤍 메소드 동적 바인딩 🤍

함수 호출 위치 결정 방식 이해하기

정적 바인딩 → 컴파일 시점에 결정, 참조 형식에 의한 결정
동적 바인딩 → 객체 전달 시점에 결정

🤍 코드 집중화와 추상화 🤍
추상화 : 만들고자 하는 객체에 대한 공통 분모 (Circle, Rect, Line = 객체, Shape = 공통 분모), 즉 추상화라는 것은 '공통분모화'라고 할 수 있다.
코드 집중화 : 재사용 ❌

코드 추상화 : 코드 집중화 ❌ → 서비스 집중화(캡슐 단위의 공통 서비스)

추상클래스를 만드는 두가지 상황 - 1

일괄처리

추상클래스를 만드는 두가지 상황 - 2

각 클래스는 공통분모가 된다.

🤍 추상 클래스 만들기 추상화 🤍

미래의 공통 클래스는 '뼈대화'라고도 할 수 있다.
뼈대가 객체화가 되면 ❌, Shape은 공통분모로 나왔기에 실체화가 되면 ❌


매번 새로 만들지 않고, 기존에 있던 Exam을 틀을 사용한다.

뼈대로 만들기 1 : Exam을 누군가가 new에서 만들지 못하록 하는 것이다. (부모로만 쓸 수 있게끔 만든다.)

part3.ex5.추상화

TestProgram.java

package part3.ex5.추상화;

public class TestProgram {
	public static void main (String[] args) {
    	
        Exam exam = new Exam(1,1,1); // Exam 은 부모클래스로만 사용.
        
        System.out.println(exam.total());
    }
}

Exam.java

package part3.ex5.추상화;

public abstract class Exam { // abstract 를 사용해서 Exam을 부모클래스로만 사용하고, Exam.java의 Exam은 추상화로만 사용한다.
	int kor;
    int eng;
    int math;
}
public Exam() {
	this(0,0,0);
}
...

OOPJavaPrj 에서 part3.ex.추상화 Package 추가
Program.java 추가

package part3.ex5.추상화;
public class Program {
	public static void main(String[] args) {
    	
    }
}

OOPJavaPrj 에서 part3.ex.추상화 패키지에
NewlecExam.java 추가

package part3.ex5.추상화;
public class NewlecExam extends Exam {
	NewlecExam exam = new NewlecExam(); 
 }

🤍 추상 메소드 (Abstract Method) 🤍

추상 클래스는 공통 자료형이다.

↓ 공통분모를 통해 코드를 간결하게 만들 수 있다.


Shape의 공통기능에는 paint(); 기능이 없기 때문에 오류가 발생한다.

오류 수정 방법 : Shape(공통분모)에 paint()라는 추상메서드를 추가하면 오류가 해결된다.

🤍 추상 메소드 구현하기 (Abstract Method) 🤍

추상화를 위한 과제 (앞으로 확장될 부분을 확인하고 자식에게 위임하기)
구현을 강제화 시킨다.

NewlecExam.java

package part3.ex5.추상화;

public class NewlecExam extends Exam {

	private int exam;
    
    public NewlecExam() {
    	this(0,0,0,0);
    }
    public NewlecExam(int kor, int eng, int math, int com) {
    	super(kor, eng, math);
        this.com = com
    }
    
    public int getCom() {
   		return com;
    }
    public int setCom(int com) {
    	this.com = com;
    }
    
	@Override
    public int total() {
    	int total = onTotal()+com;
    	return total;
    }
    @Override
    public float avg() {
    	return total()/4.0f;
    }
}

Exam.java

...
public void setEng(int eng){
	this.eng;
}
public int getMath(){
	return math;
}
public void setMath(int math){
	this.math = math;
}
public abstract int total();

// delete {return kor+eng+math}

protected int onTotal() { // protected는 자식에게만 보여줄 수 있는 함수.
	return kor+eng+math;
}
public float avg();

// delete {return total()/3.0f;
}

🤍 팩토리 메소드 (Factory Method) 🤍

🤍 팩토리 메소드 구현하기 🤍
ExamConsole.java

package part3.ex5.추상화;

import java.util.Scanner;

public abstract class ExamConsole {
	private ExamList list;
    
    public ExamConsole() {
    	list = new ExamList();
    }
    public void print() {
    	print(list.size());
    }
    public void print(int size) {
    	System.out.println("성적출력");
        System.out.println();  
    }
}

...

public void input(){
	Scacnner scan = new Scanner(System.in);
    System.out.println("성적 입력");
    System.out.println();
   
...

Exam exam = makeExam(); // 추상메서드
  exam.setKor(kor);
  exam.setEng(eng);
  exam.setMath(math);

/*--add---*/
  list.add(exam);
  
protected abstract Exam makeExam();

OOPJavaPrj → part3.ex5.추상화
NewlexExamConsole.java

package part3.ex5.추상화;

public class NewlecExamConsole extends ExamConsole {
	@Override 
    public void input() {
    	super.input();
    }
   
    @Override
    protected Exam makeExam() {
    	return new NewlecExam();
    }
}

Program.java

package part3.ex5.추상화;

public class Program {
	public static void main(String[] agrs) {
    	ExamConsole console = new NewlecExamConsole(); 
        console.input();
        console.print();
    }
}

🤍 이벤트 메소드 🤍


앞으로 계속 만들게 될 ExamConsole 개체들 … 을 위한 추상화


ExamConsole에 #onInput() 라는 추상메서드 추가


추상 메소드로 이벤트 메소드를 정의

ExamConsole.java

public abstract class ExamConsole {
	private ExamList list;
    
    public ExamConsole() {
    	list = new ExamList();
    }
    
    public void print() {
    ...
    public void print(int size) {
    	System.out.println("성적 출력 : ");
        System.out.println();
        
        for(int i=0;i<size;i++) {
        	Exam exam = list.get(i);
            int kor = exam.getKor();
            int eng = exam.getEng();
            int math = exam.getMath();
            
            int total = exam.total();
            float avg = exam.avg();
            
            System.out.printf("국어 : %d\n", kor);
            System.out.printf("영어 : %d\n", eng);
            System.out.printf("수학 : %d\n", math);
            
            onPrint(exam); // event method
            
            System.out.printf("총점 : %3d\n", total);
            System.out.printf("평균 : %6.2f\n", avg);
            System.out.println("----";
        }
    }
    
    public void input() {
    	Scanner scan = new Scanner (System.in);
        System.out.println("성적 입력 : ");
        System.out.println();
        
        int kor, eng, math;
    ...
	
    do{
   		System.out.printf("수학 : ");
        math = scan.nextInt();
        
        if(math < 0 \\ 100 < math) 
        	System.out.println("수학성적은 0~100까지 입니다.");
    } while (math < 0 \\ 100 < math);
    

    
    Exam exam = makeExam();
    exam.setKor(kor);
    exam.setEng(eng);
    exam.setMath(math);
    
    onInput(exam); // event method
    
    /*---add----*/
    
    list.add(exam);
}   

protected abstract void onPrint(Exam exam); // onPrint(exam);의 Parameter 추가
protected abstract void onInput(Exam exam); //onInput(exam);의 Parameter 추가

protected abstract Exam makeExam();

}
    

NewlecExamConsole.java

package part3.ex5.추상화;

public class NewlecExamConsole extends Exam {

	@Override 
    protected Exam makeExam() {
    	return new NewlecExam();
    }
    
    @Override 
    protected void onPrint(Exam exam) {
    	NewlecExam newlecExam = (NewlecExam)exam;
    	int com = newlecExam.getCom();
    	System.out.printf("컴퓨터 : %d\n", com);
    }
    
    @Override 
    protected void onInput(Exam exam) {
    	Scanner scan = new Scanner (System.in); 
        int com;
       
        
    	do{
   			System.out.printf("컴퓨터 : ");
        	com = scan.nextInt();
        
        	if(com < 0 \\ 100 < com) 
        		System.out.println("컴퓨터성적은 0~100까지 입니다.");
    	} while (com < 0 \\ 100 < com);
        
        newlecExam.setCom(com);
    }
}

program.java

package part3.ex5.추상화;

public class Program {
	public static void main(String[] args) {
    	// Exam exam = new NewlexExam();
        
        Exam Console = new NewlecExamConsole();
        console.input();
        console.print();
    
    }
}

🤍 코드 분리와 인터페이스 🤍

Interface = '코드 분리'의 의미 / 분리되어있는 객체를 사용할 수 있게 만들어준다.


'코드 분리'는 '참조 변수의 다형성'이라고도 불린다.


코드의 분리를 생각할 때는 일단 약속된 인터페이스로 제품을 만든다.
JAVA에서는 인터페이스를 위와같이 정의한다.
interface라는 키워드와 함께 interface의 이름을 정한다.
interface를 이용해서 스마트폰(배터리x)을 만드는 것을 마무리짓는다.


객체화 된 구현화 'Battery'라는 인터페이스를 통해서 'NewlecBattery'객체를 사용할 수 있게 된다.

🤍 추상클래스와 인터페이스의 차이 🤍
Interface는 다양한 interface를 구현할 수 있다.

공통분모 : 개체들의 공통분모를 일반화한 이름이다.


교차도 가능하다.
위의 사진에서 Interface의 주는 'FileSaver'가 된다.


상속으로 따지자면, Exam은 'Stroble'과 'Calculatable'을 상속받아 '다중상속'이라고도 한다.

클래스 상속 : 'C'를 갖고있는 나의 족보체계의 유전자. (나의 유전자를 물려받는 상속)
인터페이스 상속 : 필요에 따라서 결합하는 관계. (사교적인 관계를 만들어내는 상속)

🤍 객체 결합을 위한 인터페이스 🤍
개체 단위로 코드를 분리하는 경우

❕ 앞으로 대체하기를 원하는 부분 : 캡슐


좌측 : 인터페이스 정의, 사용 / 우측 : 인터페이스 구현

❕ 앞으로 대체하기를 원하는 부분 : 일부 기능


인터페이스를 구현하는 것이 만들어졌을 때 사용할 수 있는 형태를 준비해야한다.

❕ 앞으로 대체하기를 원하는 부분 : 구현코드의 일부


인터페이스를 정의하고, 인터페이스를 구현화한 후, 정의된 곳에 사용한다. (캡슐화)


인터페이스는 캡슐화 뿐만 아니라 인터페이스 형식으로도 사용가능하다.


OOPJavaPrj/src → Package : part3.ex6.인터페이스 / Program.java 생성

package part3.ex6.인터페이스;

public class Program {
	public static void main(String[] args {
    	A a = new A();
        a.print();
    }
}

A.java

package part3.ex6.인터페이스;

public class A {
	private B b;
    
    public A() {
    	b = new B();
    }
    
    public void print() {
    	int total = b.total();
        
        System.out.printf("total is %d\n", total);
    }
}

B.java

package part3.ex6.인터페이스;

public class B {
	public int total() {
    	return 30;
    }
}

🤍 개체 결합력을 낮추는 코드로 변경하기 🤍

A와 B의 결합력이 높음 : B를 대체할 수 없게 만든 구조

객체간에 결합형으로 만들어질 수 밖에 없는 상황 : 나중에 B를 주입할 수 있게 하기

객체간에 결합형으로 만들어질 수 밖에 없는 상황 : 나중에 C로도 대체 가능하게 하기

유전자가 달라도 기능이 맞다면 대체할 수 있다. ('다형성'이라고도 한다.)


program.java

package part3.ex6.인터페이스;

public class Program {
	public static void main(String[] args {
    	A a = new A();
        B b = new B();
        a.setX(b); // 위의 두코드(A와 B)를 결합시키는 코드
     
        a.print();
    }
}

A.java

package part3.ex6.인터페이스;

public class A {
	private X x; // interface 사용, 현재 null이기 때문에 참조할 수 있는 것이 없기에 추후에 참조할 수 있도록 setter을 생성한다. 
    
    public void setX(X x) {
    	this.x = x;
    }
    
    public A() {
    	
    }
    
    public void print() {
    	int total = x.total();
        
        System.out.printf("total is %d\n", total);
    }
}

B.java

package part3.ex6.인터페이스;

 public class B implements X { // B기 X를 구현했다는 의미
	public int total() {
    	return 30;
    }
} 

X.java

package part3.ex6.인터페이스;

public interface X {
	int total(); // 구현을 하는 것이 아니기 때문에 중괄호가 없다. interface는 참조만 가능하다.
}

🤍 새로운 객체로 변경하기 🤍

C 클래스(설정파일)가 B 클래스를 대체한다.


설정파일
1. .xml 파일 형태의 외부설정 파일
2. @Annotation 은 클래스에 컴파일해도 남겨지는 주석파일

program.java

package part3.ex6.인터페이스;

public class Program {

	public static void main(String[] args throws IOException, ClassNotFoundException ,InstantiationException {
    	FileInputStream fis = new FileInputStream("OOPJavaPrj/src/part3/ex6/인터페이스"/setting.txt); //FileInputStream을 이용하여 ClassName을 읽을 수 있게한다. 
        Scanner sacn = new Scanner(fis);
    	String className = scan.nextLine();
        System.out.println("className");
        scan.close();
        fis.close();
        
❕ // Class.forName 을 통해 class의 정보를 알 수 있고, 그 clazz 정보에 대해서 새로운 인스턴스 (X x = (X) clazz newInstance();) 를 만들 수 있다. ❕
        
        Class clazz = Class.forName(className); // 'Class.forName'은 현재 사용하고 있는 class에 대한 정보를 얻게되는 구조체.
    	a = new A();
      ❕ X x = (X) clazz.newInstance();
        a.setX(x); 
     
        a.print();
    }
}

A.java

package part3.ex6.인터페이스;

public class A {
	private X x; // 
    
    public void setX(X x) {
    	this.x = x;
    }
    
    public A() {
    	
    }
    
    public void print() {
    	int total = x.total();
        
        System.out.printf("total is %d\n", total);
    }
}

B.java

package part3.ex6.인터페이스;

 public class B implements X { 
	public int total() {
    	return 30;
    }
} 

X.java

package part3.ex6.인터페이스;

public interface X {
	int total(); 
}

OOPJavaPrj → setting.txt 파일 생성

part3.ex6.인터페이스.B
→ part3.ex6.인터페이스.C

OOPJavaPrj → C.java 파일 생성

package part3.ex6.인터페이스;

public class C implements X{
	
    @Override
    public int total() {
    	return 50;
    }
}

🤍 일부 기능을 분리하는 인터페이스 🤍

GamePrj
GameFrame.java

import java.awt.Frame;

public class GameFrame extends Frame {

	@Override
    public void paint(Graphics g) {
    	super.paint(g);
        
        g.drawRect(100, 100, 200, 100);
    }
}

Program.java

import java.awt.Frame;

public class Program {
	public static void main (String[] args) {
    	GameFrame frame = new GemeFrame();
        WindowListener listener = new GameWindowListener();
        frame.addWindowListener(listener); // WindowListener 인터페이스를 구현해달라고 인터페이스를 정의해놓고있다. 
        frame.setVisible(true);
    }
}

WindowListener를 구현하기 위해 GameWindowListener클래스 생성

GameWindowListener.java

public class GameWindowListener implements WindowListener {
	
    @Override 
    public void windowActivated(WindowEvent e) {
    
    }
    @Override 
    public void windowClosed(WindowEvent e) {
    
    }
    @Override 
    public void windowClosing(WindowEvent e) {
    	JOptionPane.showMessageDialog(null ,"Good bye");
        // null : 바탕화면 기준으로 중앙에 팝업을 띄우는 것을 의미한다. 
        System.exit(0); // ❕ 상태값에 0은 "정상종료"를 의미하고, 다른값을 넣는 경우엔 오류가 있다.라고 runtime에게 알려준다. ❕
    }
    @Override 
    public void windowDeactivated(WindowEvent e) {
    
    }
}

🤍 인터페이스 구현하는 위치문제 🤍

인터페이스를 구현하는 클래스의 위치 4-1

인터페이스는 새로운 클래스를 생성해서 사용하는 것보다는 인터페이스를 구현하는데에 있어서 필요한 자원에 있는 곳에 구현하는 것이 이상적이다.


GameFrame.java (java프로그램 기준으로 중앙에 팝업을 띄우기 위한 코드)

import java.awt.Frame;

public class GameFrame extends Frame ❕ implementes WindowLinstener ❕ {

	// 생성자 추가
	public GameFrame() {
    	// WindowListener listener = new GameWindowListener();
        // 이미 implements WindowListener에서 구현했기에 윗코드는 없어도 된다.
        addWindowListener(this); 
        // Program.java에서 GameFrame frame = new GameFrame();에서 
        // frame을 GameFrame으로 넘겨받았기 때문에 'this'를 사용한다.
        // this는 생략가능하다.
        // this = GameFrame 을 의미한다. 
        setVisible(true);
    }
	
    @Override
    public void paint (Graphics g) {
    
    	super.paint(g);
        
        g.drawRect(100, 100, 200, 100);
    }
    @Override 
    public void windowActivated(WindowEvent e) {
    
    }
    @Override 
    public void windowClosed(WindowEvent e) {
    
    }
    @Override 
    public void windowClosing(WindowEvent e) {
    	JOptionPane.showMessageDialog(❕this❕,"Good bye");
        // GameFrame.java에서 괄호의 this를 사용하게 되면 윈도두의 가운데에 창이 뜬다.
        System.exit(0);
    }
    @Override 
    public void windowDeactivated(WindowEvent e) {
    
    }
}

Program.java

import java.awt.Frame;

public class Program {
	public static void main (String[] args) {
    	GameFrame frame = new GemeFrame();
        //WindowListener listener = new GameWindowListener();
        //frame.addWindowListener(listener); 
        //frame.setVisible(true);
    }
}

출처 : 링크텍스트

profile
미래의 웹프로그래머

0개의 댓글