JAVA | New Lecture 강의 - 객체지향 프로그래밍

여경·2021년 5월 31일
0

CS

목록 보기
16/16


21/05/31

1. 캡슐화 -> 인스턴스, 클래스화


책임을 전가 -> 역할자로서의 객체 = 객체 지향
인스턴스, 클래스화

인스턴스메소드 형식으로 구현

  • 객체를 먼저 앞세워서 작성하기 때문에 훨씬 편하다는 느낌!
switch(menu) {
	case 1:
         	//ExamList.inputList(list);
              list.inputList(); 
               //인스턴스메소드 형식으로 구현
                 break;
void inputList() {
                Scanner scan = new Scanner(System.in);
                System.out.println("성적입력");
                int kor, eng, math;

                //{... 중략}

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

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

                if (exams.length == size) {
                        //1. 크기가 더 큰 배열을 생성하기
                        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++;

        }

inputlist 메소드 코드에서 변화한 부분

  1. 인스턴스메소드임을 알려주기 위해 static 제거
  2. list.exams, list.current 등 이미 list의 메소드임을 선언했기에 this.exams, this.current 등으로 선언 변경

접근제어 지시자와 캡슐 깨기

접근제어지시자를 통해 클래스 내부의 기능 사용에 제한을 줄 수 있다.

 private int current;
//이 클래스 외에 다른 클래스에서 current를 선언해도 문제가 발생하지 않는다.

생성자와 객체 초기화

변경 전

public static void init(ExamList list) {
                list.exams = new Exam[3];
                list.current = 0; 
}

변경 후

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

변경의 이유?

객체가 생성되고나서 객체를 실체화하기 위해 선언해줘야할 값을 init하여, 함수를 따로 만들어서 사용하는 방식을 이용하였었음. 하지만 예시의 ExamList처럼 생성자라는 초기화를 위한 특별한 함수가 필요하다.

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

호출과 동시에 초기화 하기 때문에 Init의 역할을 하게 된다. 생성자 ExamList는 반환의 목적이 아니기때문에 반환값이 없다. 만들어진 객체를 넘거받는 용도, 그리고 중요한 것은 사실 이것은 함수 이름이 아니다. 생성자는 함수명이 없다. 초기화할 객체를 한정하기 위해 사용되는 명칭일 뿐이다!

생성자 오버로드

때에 따라 사이즈를 넘겨 받아서 그 사이즈의 갯수만큼 배열을 초기화 하고 싶은 생성자를 작성해보고자 한다.
생성자도 함수의 특징을 갖기 때문에 파라미터를 넘겨 받을 수 있어서 생성자를 두가지 방식으로 호출할 수 있다.

  1. new ExamList();
    기본값 생성자
  2. new ExamList(10);
    원하는 만큼 배열 준비하는 생성자 (오버로드 생성자)

주의사항
1. 기본 생성자가 없는 상황에서 오버로딩한 생성자만 만들었을 때 기본 생성자를 호출할 수 없다.
2. 생성자를 오버로드 한다는 것은 똑같은 기능을 인자를 다르게 해서 편의를 도모하는 것이다. 똑같은 기능을 구현하고 있기 때문에 코드 중복을 제거해야한다. 기본 생성자에서 오버로드 생성자를 호출하여 중복을 제거할 수 있다. 생성자를 호출하는 방법은 방금 생성된 객체를 통해서만 호출할 수 있다.

2-1. 호출하는 방법ExamList()가 ExamList(int size)를 호출하려 하는 상황이다. this(); 이라는 객체를 통해 괄호에 값을 넣으면, 인자를 갖고 있는 생성자로 값이 전이가 된다. 값을 넘겨주지 않으면 자신이 자신을 호출하는 일이 발생한다.
3. 생성자를 정의하지 않았을 때 new ExamList(); 에서 () 을 통해서만 객체를 생성했었기 때문에 정의하지 않아도 기본 생성자를 저절로 컴파일러가 만들어준다.


2. Getters와 Setters

get

 int kor = exam.getKor(); //exam.kor;     int eng = exam.getEng();
int math = exam.getMath();
 public int getKor() {
        return kor1;
    }

    public int getEng() {
        return eng;
    }

    public int getMath() {
        return math;
    }

set

  Exam exam = new Exam();
                exam.setKor(kor); //exam.kor = kor;
                exam.setEng(eng); //exam.eng = eng;
                exam.setMath(math); //exam.math = 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;
    }

이렇게 왜 설정해야 하는 가?
캡슐화란 데이터 구조를 사용하는 녀석들을 모아논 것.
데이터 구조가 변경되면서 종속되어있는 문제들을 해결하려고 하는 행동. 속성명이 변경 되는 것을 의미하는 것이 아니었음.

-> 속성이 달라진 건 아니지만 구조적 변화를 가진 것.

3. dependency, 속성, 상속

사용하는 모든 객체는 dependency

다만 관계를 가지고 있다고 하려면 그것은 최소한 멤버의 속성(Has - A 관계)으로 있거나 상속(Is - A 관계)을 해야만 관계가 있다고 함.


21/06/04
머? 어제꺼가 없다고요? 에헴
코드 재사용 / IS A 상속 / Override 메소드 / 자식 클래스의 객체 초기화

코드 재사용

제품을 배포할 때

  1. 컴파일 -> Exam.class이 만들어진다.
  2. 이것을 압축 -> Exam.zip
  3. 파일명을 java를 위한 압축파일이라는 뜻으로 확장자를 jar로 변경한다 -> Exam.jar (javaarchive)
  4. 배포파일 만드는 기능을 사용 -> Export 사용 ( inteliJ에서는 artifacts에 있음)

뉴렉처 강의에서는 이클립스를 사용하여 강의하기 때문에 인텔리제이에서 import, export 하는 방법을 따로 찾아보아 사용했다!!
참고한 링크 : jar 파일 Export, Import 하기 (inteliJ)

IS A 상속

public class program {
    public static void main(String[] args) {
    
        //Is A 상속 - 부품의 향기를 전혀 느끼지 못하고, 직접 구현한 것 같이 느껴지게 하는 함수가 됨.
        NewlecExam exam = new NewlecExam();
        exam.setKor(10);
        exam.setEng(10);
        exam.setMath(10);
        exam.setCom(10);

        System.out.println(exam.total());
        // IS A 상속에서 생기는 문제점 - total 기능을 실행했을 때 이전에 있던 total 대로 실행될 뿐 새로 만든 클래스에서 구현한 SetCom 의 값의 합까지
        //더해진 total 값으로 출력되지 않았음.
    }
}

Is A 상속 - 부품의 향기를 전혀 느끼지 못하고, 직접 구현한 것 같이 느껴지게 하는 함수가 됨.
IS A 상속에서 생기는 문제점 - total 기능을 실행했을 때 이전에 있던 total 대로 실행될 뿐 새로 만든 클래스에서 구현한 SetCom 의 값의 합까지 더해진 total 값으로 출력되지 않았음.

이 문제를 해결하기 위해서 ..!

Override 메소드

public class program {
    public static void main(String[] args) {
        NewlecExam exam = new NewlecExam();
        exam.setKor(10);
        exam.setEng(10);
        exam.setMath(10);
        exam.setCom(10);

        System.out.println(exam.total());
        System.out.println(exam.avg());
       }
}
//바이너리 형태로 import 해온 Exam을 물려받음
public class NewlecExam extends Exam {
    //Exam을 부품으로 사용한다면 안 쪽에 이런 코드를 작성하게 됨.

    // private Exam exam; //Has A 상속

    private int com;
    public int getCom() {
        return com;
    }

    public void setCom(int com) {
        this.com = com;
    }

    public int total() {
        return super.total() + com; //super - NewlecExam 에서 부모 객체 영역에 해당하는 total이 호출됨.
    }

    public float avg() {
        return total()/4.0f;
    }
}

왜 위와 같은 override의 문제점이 발생하는가?

먼저, 국어 영어 수학이라는 값을 저장할 수 있는 공간 확보가 필요하다. 이게 없으면 NewlecExam이 의미가 없고 com이라는 과목이 저장될 수 있는 공간이 이후에 필요하게 된다. 국어 영어 수학은 결국 Exam이라는 객체와 같기에exam.total을 호출하게 되면 국영수만 들어있는 super를 호출하게 된다. 해결하는 방법은 NewlecExam 클래스에 total을 정의해놓는다면 Exam까지 가지 않고 override된 (우선순위) 메소드에서 값을 가져온다.

override method: 부모가 가지고 있는 기능을 가리는 메소드, 그래서 재정의하여 잘못된 부분을 고쳐쓸 수 있는 메소드.

하지만 코드 상에서 그냥 전처럼 편하게

NewlecExam exam = new NewlecExam(1,1,1,1);

위와 같은 식으로 넘겨주고 싶다면...??

public class program {
    public static void main(String[] args) {
       NewlecExam exam = new NewlecExam(1,1,1,1);
        exam.setKor(10);
        exam.setEng(10);
        exam.setMath(10);
        exam.setCom(10);

        System.out.println(exam.total());  
        System.out.println(exam.avg());
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); //super를 사용하면 부모에 있던 생성자가 호출됨
        this.com = com;
    }

    public int getCom() {
        return com;
    }

    public void setCom(int com) {
        this.com = com;
    }

    public int total() {
        return super.total() + com; //super - NewlecExam 에서 부모 객체 영역에 해당하는 total이 호출됨.
    }

    public float avg() {
        return total()/4.0f;
    }
}

부모 생성자를 호출하여 기존의 것(국영수)을 세팅하게 하고, 내가 확장했던 것(컴퓨터과목)만 새로 만든 곳에서 초기화 하도록 하는게 제일 바람직하다.

정리:

상속받고 있는 클래스에서 무언가를 초기화 하는데 그 부분의 일부분이 부모의 것이라고 할 때 부모의 영역에 있는 것이 호출되어 확장된 부분만 내가 세팅하면 된다. 부모 객체는 부모의 기능으로 초기화한다.

4. 추상화, 집중화

코드의 집중화 / 추상화

추상화(일반화) : 코드 집중화(x) -> 서비스 집중화 (캡슐 단위의 공통 서비스
캡슐이 가지고 있는 서비스가 공통될 때 캡슐을 만들 때마다 같은 코드를 작성할 필요 없이 하나의 공동 개체로 묶어 캡슐 단위의 공통 기능을 집중화한다. (IS A 상속)

추상화 클래스를 만드는 상황(1)
-> 도형이라는 공통적인 것으로 묶여 집중화 시키는 예

추상화 클래스를 만드는 상황(2)

Exam이 공통 분모화 (추상화)가 되지 않음.

공통 서비스화로 얻을 수 있는 장점
1. 코드 집중화 - 반복되는 코드를 한 곳에 정리하기 때문에
2.일괄처리 - 일괄적으로 관리 가능. 일괄적으로 배열에 담으려고 할 때 추상클래스가 없다면 한번에 배열에 담을 수 있는 자료형이 있지만 (object class) , 아직은 모르기 때문에 각각 마련해야한다는 단점이 있음. -> 원 배열, 직사각형 배열 등 다 따로 구현하는 상황

-> shape라는 부모 객체에 일괄적으로 포함시켜 호출 가능 (공통분모화)

0개의 댓글