본 포스트는 카카오 테크 캠퍼스 1기에서 제공하는 패스트캠퍼스 강의에서 배운 내용을 토대로 정리하였습니다.
구현 코드 없이 메서드의 선언만 있는 추상 메서드(abstract method)를 포함한 클래스
메서드 선언(declaration) : 반환타입, 메서드 이름, 매개변수로 구성 => 선언부만 가짐
구현부(body) 안가짐
메서드 정의(definition) : 메서드 구현(implementation)과 동일한 의미
구현부(body) 를 가짐 ({ })
예)
int add(int x, int y);
// 메서드 선언. 선언부만 있음. 추상 메서드임
int add(int x, int y){ }
// 메서드 정의(구현). 구현부와 선언부가 있음. 추상 메서드아님
abstract 예약어를 사용
추상 클래스는 new 할 수 없음 ( 인스턴스화 할 수 없음 )
메서드에 구현 코드가 없으면 abstract 로 선언
abstract로 선언된 메서드를 가진 클래스는 abstract로 선언
모든 메서드가 구현 된 클래스라도 abstract로 선언되면 추상 클래스로 인스턴스화 할 수 없음
추상 클래스의 추상 메서드는 하위 클래스가 상속 하여 구현
추상 클래스 내의 추상 메서드 : 하위 클래스가 구현해야 하는 메서드
추상 클래스 내의 구현 된 메서드 : 하위 클래스가 공통으로 사용하는 메서드 ( 필요에 따라 하위 클래스에서 재정의 함 )
다음과 같은 예제 시나리오를 구현 해보자.
위 사진에서 이탤릭체로 되어 있는 메소드는 추상메서드 이다.
컴퓨터는 크게 데스크탑과, 노트북 으로 나눌 수 있고, 모든 컴퓨터 기기가 기본적으로 반드시 제공되야할 기능들(전원 키고 끄기, 글자 입력, 화면 보이기)을 최상위 클래스인 컴퓨터에 선언한다.
이때 전원을 키고 끄는거는 컴퓨터마다 똑같이 떄문에 구현부까지 선언하고, 그외 타이핑하는거나 디스플레이하는 거는 기기마다 다를 수 있기에 추상 메서드로 선언했다.
Computer.java
public abstract class Computer {
abstract void display();
//하위 클래스에서 구현하도록 추상 메서드로 선언
abstract void typing();
//하위 클래스에서 구현하도록 추상 메서드로 선언
public void turnOn() {
//기기가 분화되도 기능은 변함없기 때문에 최상위 클래스에서 구현.
//물론 하위 클래스에서 재정의 가능
System.out.println("전원을 켭니다.");
}
public void turnOff() {
//기기가 분화되도 기능은 변함없기 때문에 최상위 클래스에서 구현.
//물론 하위 클래스에서 재정의 가능
System.out.println("전원을 끕니다.");
}
}
DeskTop.java
public class DeskTop extends Computer{
@Override
void display() {
//가상 메서드 구현
System.out.println("DeskTop display");
}
@Override
void typing() {
//가상 메서드 구현
System.out.println("DeskTop typing");
}
@Override
public void turnOff() {
//상위클래스의 구현된 메서드 재정의
System.out.println("Desktop turnoff");
}
}
NoteBook.java
public abstract class NoteBook extends Computer{
//Computer 클래스의 추상 메서드 2개중 1개만 구현했기 때문에,
//반드시 추상클래스로 선언해야함.
//추상클래스이므로 인스턴스 생성 불가.
@Override
public void typing()
//가상 메서드 구현
System.out.println("NoteBook typing");
}
}
MyNoteBook.java
public class MyNoteBook extends NoteBook{
//Computer 클래스의 추상 메서드 모두를 구현 완료.
@Override
void display() {
//가상메서드 구현
System.out.println("MyNoteBook display");
}
}
ComputerTest.java
public class ComputerTest {
public static void main(String[] args) {
Computer computer = new DeskTop();
computer.display();
computer.turnOff();
NoteBook myNote = new MyNoteBook();
//NoteBook baseNote = new NoteBook(); 는 오류가남,
// 추상클래스 를 구현하려 했기 때문
}
}
알고리즘의 골격을 정의한 메서드를 의미한다.
시나리오의 흐름을 step별로 정의한 메서드 이기도 하다.
템플릿 메서드 패턴은 부모 클래스에서 코드의 흐름(시나리오)을 스텝(단계)별로 정의하고,
해당 시나리오의 흐름을 변경하지 않고 필요시 자식 클래스들이 알고리즘의 특정 단계들을 오버라이드(재정의)해서 시나리오를 구체화 하는 설계 패턴을 말한다.
보통 시나리오(템플릿 메서드)는 변경되지 않기위해 final로 선언하여 하위 클래스에서 재정의 할 수 없게 함
프레임워크에서 많이 사용되는 설계 패턴
추상 클래스로 선언된 상위 클래스에서 템플릿 메서드를 활용하여 전체적인 흐름을 정의 하고,
하위 클래스에서 다르게 구현되어야 하는 부분은 추상 메서드로 선언하여 하위 클래스에서 구현 하도록 함
위 와 같은 시나리오를 템플릿 메서드 패턴을 이용해 설계 해보자.
차의 시동을 키고 끄는 것은 공통적인 성능이므로 구현까지하고,
운전이나 차가 멈추는건 차의 종류에따라 다르므로 추상메서드로 선언하고 하위클래스에서 구체화 하도록한다.
Car.java
public abstract class Car {
public abstract void drive();
//추상 메서드로 선언
public abstract void stop();
//추상 메서드로 선언
public void startCar() {
//구현 해놓는다.
System.out.println("시동을 켭니다.");
}
public void turnOff() {
//구현 해놓는다.
System.out.println("시동을 끕니다.");
}
final public void run() {
//템플릿 메서드 구현
//final 키워드로 변경 불가능하게 해놓는다.
startCar();
drive();
stop();
turnOff();
}
}
ManualCar.java
public class ManualCar extends Car{
@Override
public void drive() {
//부모의 가상 메서드 구현
System.out.println("사람이 운전합니다.");
System.out.println("사람이 핸들을 조작합니다.");
}
@Override
public void stop() {
//부모의 가상 메서드 구현
System.out.println("브레이크를 밟아서 정지합니다.");
}
}
AICar.java
public class AICar extends Car{
@Override
public void drive() {
//부모의 가상 메서드 구현
System.out.println("자율 주행합니다.");
System.out.println("자동차가 스스로 방향을 바꿉니다.");
}
@Override
public void stop() {
//부모의 가상 메서드 구현
System.out.println("스스로 멈춥니다.");
}
}
CarTest.java
public class CarTest {
public static void main(String[] args) {
// 다형성을 볼수 있는 부분
// 각자 자식클래스를 부모클래스자료형에 넣어서 구현
// (업캐스팅)
Car aiCar = new AICar();
aiCar.run();
// 템플릿메서드인 run을 통해 일관된 시나리오 작동
System.out.println("=================");
Car manualCar = new ManualCar();
manualCar.run();
// 템플릿메서드인 run을 통해 일관된 시나리오 작동
}
}
결과는 아래와 같다.
final 변수 : 값이 변경될 수 없는 상수
public static final double PI = 3.14;
final 메서드 : 하위 클래스에서 재정의 할 수 없는 메서드
final 클래스 : 상속할 수 없는 클래스
Define.java
public class Define {
public static final int MIN = 1;
public static final int MAX = 999999;
public static final double PI = 3.14;
public static final String GREETING = "Good Morning!";
public static final int MATH_CODE = 1001;
public static final int CHEMISTRY_CODE = 1002;
}
UsingDefine.java
public class UsingDefine {
public static void main(String[] args) {
System.out.println(Define.GREETING);
System.out.println(Define.MIN);
System.out.println(Define.MAX);
System.out.println(Define.MATH_CODE);
System.out.println(Define.CHEMISTRY_CODE);
System.out.println("원주률은" + Define.PI + "입니다.");
}
}