학생들이 교실청소를 분업해서 진행해야 하는 상황 가정
진행해야 하는 작업은 총 3가지 (바닥쓸기, 창문닦이, 칠판닦이)
아래로 내려갈수록 점점 코드가 발전하는(체계화되는) 방식
절차지향 프로그래밍
- 학생 한명 한명에게 명령어를 직접 쳐서 바닥쓸기, 창문닦이, 칠판닦이를 시킴
- 같은 작업인데도 지정하는 학생이 달라질 때 마다 코드를 똑같이 다시 쳐야하기 때문에 중복이 심함
- 반복문으로 중복코드를 조금 없앰
- 학생을 인자로 받을 수 있는 함수에 2에서 활용한 반복문까지 써서 중복 코드 줄임
- 여전히 중복이 많음
- 여기 3번까지가 절차지향 프로그래밍 에서 가능. 하지만 청소할 곳의 범위, 작업이 복잡해지면 프로그래밍 하기 점점 어려워짐. 이제부턴 객체지향 프로그래밍을 해보자
객체지향 프로그래밍
클래스
- 클래스 만들어서 진행
- 클래스 선언
public class 창문닦이{ private ClassRoom 담당_교실; private int 행주_깨끗함 = 100; private double 세제_남은리터 = 10.0; public 창문닦이(ClassRoom 교실){ 담당_교실 = 교실; } public void 참문_닦기(){ while (!담당_교실.창문_깨끗함 ()){ if (행주_깨끗함 <=10 ) 빨아오기(행주_깨끗함); if (세제_남은리터 <= 1) 리필해오기(세제_남은리터); 담당_교실.창문_닦기(행주_깨끗함, 세제_남은리터); } } } public class 칠판닦이{ private ClassRoom 담당_교실; private int 지우개_깨끗함 = 0; public 칠판닦이(ClassRoom 교실){ 담당_교실 = 교실; } public void 칠판_닦기(){ while (!담당_교실.칠판_깨끗함 ()){ if (지우개_깨끗함 <=10 ) 털기(지우개_깨끗함); } } } public class 바닥쓸이{ private ClassRoom 담당_교실; private double 쓰레받기_채움 = 0; public 바닥쓸이(ClassRoom 교실){ 담당_교실 = 교실; } public void 바닥_쓸기(){ while (!담당교실.바닥_깨끗함 ()){ if (쓰레받기_채움 >= 0.9) 비우기(쓰레받기_채움); 담당_교실.바닥_쓸기(쓰레받기_채움); } } }
창문닦이 영수 = new 창문닦이(우리학교._101호); 영수.창문_닦기(); 창문닦이 혁순 = new 창문닦이(우리학교._102호); 혁순.창문_닦기(); 바닥쓸이 철권 = new 바닥쓸이(우리학교._101호); 철권.바닥_쓸기(); 칠판닦이 변수 = new 칠판닦이(우리학교._101호); 변수.칠판_닦기(); ArrayList<칠판닦이> 칠판닦이들 = new ArrayList<>(); 칠판닦이들.add(new 칠판닦이(우리학교._102호)); 칠판닦이들.add(new 칠판닦이(우리학교._103호)); 칠판닦이들.add(new 칠판닦이(우리학교._104호)); 칠판닦이들.add(new 칠판닦이(우리학교._105호)); for (칠판닦이 닦이: 칠판닦이들) { 닦이.칠판_닦기(); }
- 은닉성(private 있는 이유)
- 객체들의 변수들을 외부에서 접근하면 예기치 못한 에러가 발생할 수 있음
- 따라서 외부가 사용가능한 메소드나 속성을 public으로 제공하고, 중요한 속성들은 private으로 감춰둠 ⇒ 은닉성
- 리모콘을 통해 TV를 조작할 수 있지만 내부에서 어떻게 작동되서 TV가 조작되는지는 우리는 모름
- 객체들의 변수들을 외부에서 접근하면 예기치 못한 에러가 발생할 수 있음
- 각 학생들은 모두 역할만 다를 뿐 다 함께 본질은 청소하는건데 위에서 보면 다른 클래스들의 청소 함수는 for문을 통해 한번에 처리가 불가능함
인터페이스
-
한 명령어로 모두에게 청소 하게끔 하도록
인터페이스
사용public interface 청소담당{ public void 청소(); public void 교실_이동 (ClassRoom 교실); } // 인터페이스에서 무슨 역할을 할지 구현하지는 않음 public class 창문닦이_v2 implements 청소담당{ private ClassRoom 담당_교실; private int 행주_깨끗함 = 100; private double 사용안한_세제= 10.0; public 창문닦이_v2 (ClassRoom 교실){ 담당_교실 = 교실; } public void 창문_닦기(){ while (!담당_교실.창문_깨끗함 ()) { if (행주_깨끗함 <= 10) 빨아오기(행주_깨끗함); if (윈덱스_리터 <= 1) 리필해오기(윈덱스_리터); 담당_교실.창문_닦기(행주_깨끗함, 윈덱스_리터); } } @Override public void 청소(){ 창문_닦기(); } @Override public void 교실_이동(ClassRoom 교실) { 담당_교실 = 교실; } } 창문닦이_v2 영수 = new 창문닦이_v2(우리학교._101호); 바닥쓸이_v2 철권 = new 바닥쓸이_v2(우리학교._101호); 칠판닦이_v2 변수 = new 칠판닦이_v2(우리학교._101호); 영수.청소(); 철권.청소(); 변수.청소();
- Override를 통해 반드시 청소, 교실이동 함수를 재정의 해줘야함. 이를통해 청소 함수만 호출하면 각 클래스들에서 본인 클래스에 맞는 청소 함수를 실행하게 됨.
ArrayList<청소담당> 청소담당들 = new ArrayList<>(); 청소담당들.add(new 칠판닦이_v2(우리학교._102호)); 청소담당들.add(new 칠판닦이_v2(우리학교._103호)); 청소담당들.add(new 바닦쓸이_v2(우리학교._104호)); 청소담당들.add(new 칠판닦이_v2(우리학교._105호)); for (청소담당 담당: 청소담당들) { 담당.청소(); }
- 주목할 부분
- ArrayList의 타입이
청소담당
으로 되어있는것 - 다른 클래스들의 청소관련 함수를
청소
메서드로 호출하는 것
- ArrayList의 타입이
-
객체지향의 강력함 등장 → 클래스들로 또 다른 클래스 조립하기
-
팀 단위로 청소당번을 움직여보자 (학생단위에서 팀 단위가 되어서 훨씬 관리하기 수월해짐)
public class 교실청소팀{ protected ClassRoom 담당_교실; private 창문닦이_v2 창문닦이_1, 창문닦이_2; private 바닥쓸이_v2 바닥쓸이_1, 바닥쓸이_2, 바닥쓸이_3; private 칠판닦이_v2 칠판닦이_1; private 청소담당[] 팀원들 = { 창문닦이_1, 창문닦이_2, 바닥쓸이_1, 바닥쓸이_2, 바닥쓸이_3, 칠판닦이_1 }; public 교실청소팀 (ClassRoom 교실){ 담당_교실 = 교실; 창문닦이_1 = new 창문닦이_v2(교실); 창문닦이_2 = new 창문닦이_v2(교실); 바닥쓸이_1 = new 바닥쓸이_v2(교실); 바닥쓸이_2 = new 바닥쓸이_v2(교실); 바닥쓸이_3 = new 바닥쓸이_v2(교실); 칠판닦이_1 = new 칠판닦이_v2(교실); } public void 교실청소 (){ // 팀원들 안에있는 클래스들이 모두 청소담당 // 인터페이스를 implements 했으므로 // 청소 메소드만으로 청소 명령 가능 for (청소담당 팀원: 팀원들){ 팀원.청소(); } } public boolean 청소완료(){ return 담당_교실.창문_깨끗함() && 담당_교실.바닥_깨끗함() && 담당_교실.칠판_깨끗함(); } public void 교실_이동 (ClassRoom 교실){ for (청소담당 팀원: 팀원들){ 팀원.교실_이동(교실); } } }
-
문제 발생! 과학실은 특별한 도구들이 있어서 특별 지식을 가지고 있는 학생들이 필요함.
-
상속
- 상속 개념 발동!
- 과학실 청소팀이라는 클래스를 통째로 새로 만들수도 있겠지만, 기존에 만들어둔 교실청소팀 클래스의 거의 모든 속성과 메소드가 같다면 중복 발생
- 즉 과학실 청소팀을 만들긴 할건데 기존 교실청소팀을 상속해서 만들면 중복을 줄일 수 있음
public class 과학실청소팀_v2 extends 교실청소팀{ 실험기구닦이 실험기구닦이_1; // 변수가 이것밖에 없는것처럼 보이지만 // 상속했으므로 교실청소팀의 모든 변수 다 가져온 상태임 public 과학실청소팀_v2(ClassRoom 교실){ super(교실); // 메모리에 교실청소팀 클래스도 인스턴스화함 실험기구닦이_1 = new 실험기구닦이(교실); } @Override public void 교실청소(){ super.교실청소(); 실험기구닦이_1.청소(); // 얘도 부모의 list 변수에 add 해놓으면 이렇게 구현할 필요가 없지 않은가? } @Ovrride public boolean 청소완료(){ return super 청소완료() && 담당_교실.실험기구_깨끗함(); } @Override public boolean 교실_이동(ClassRoom 교실){ super.교실_이동(교실); 실험기구닦이_1.교실_이동(교실); } }
- 나는 위 코드에서 public boolean 교실_이동(ClassRoom 교실) 메서드에서 실험기구닦이_1.교실_이동(교실) 메서드가 필요없다고 생각했음. 왜냐하면 ClassRoom 교실 객체는 부모 클래스에서 상속받아서 사용할 것이라고 생각했기 때문
-
- 위 교실청소팀 클래스를 상속받으면 모든 속성과 메소드를 받아서 클래스를 만들 수 있음
-
여러개 클래스가 공통으로 가지고 있는 속성 같은게 있는 문제 발생
- 과학실 청소팀이라는 클래스를 통째로 새로 만들수도 있겠지만, 기존에 만들어둔 교실청소팀 클래스의 거의 모든 속성과 메소드가 같다면 중복 발생
-
추상 클래스를 상속해서 해결!
public abstract class 교실_청소당번 { protected ClassRoom 담당_교실; public 교실_청소당번 (ClassRoom 교실) { 담당_교실 = 교실; } public void 교실_이동 (ClassRoom 교실) { 담당_교실 = 교실; } public void 청소() { // 이 청소 메소드는 자식클래스에서 다른 역할로 override, 즉 대체해서 쓸 수 있는데 이를 객체지향의 다형성 이라고 함 } }
- 이 추상클래스는 객체를 만들수 없음
- 개, 고양이 등이 포유류지만 어떤 동물을 보고 포유류라고 하지는 않음.
- 포유류는 너무 추상적인 개념
- 추상클래스는 공통분모 역할만을 위한 클래스
- 위 추상클래스를 통해서 아까 인터페이스에서 했던 것처럼 청소 메소드만으로 모든 클래스들에게 청소를 override시킨 후 바로 반복문에서 청소메소드 호출로 작업 가능
- 그러면 인터페이스는 쓸모 없는것 아닌가?
-
그렇지 않다. 상속구조는 상하 관계가 뚜렷한것인데 아래 그림처럼 날치, 박쥐, 날다람쥐는 모두 날기가 가능한 날짐승이란 카테고리에 속할 수 있음. 이를 위한 것이 인터페이스임.
-
상속은 물려받는것, 인터페이스는 장착
-
- 그러면 인터페이스는 쓸모 없는것 아닌가?
- 개, 고양이 등이 포유류지만 어떤 동물을 보고 포유류라고 하지는 않음.
PREVIOUS경제