Information Security Study
240116 추상클래스(abstract 실습), 템플릿메서드, 인터페이스, has - a 관계 본문
추상 클래스
abstract 예제
package abstract_.abs;
public abstract class PopupStore {
/*
* 1. 메서드에 abstract를 붙이면 해당 메서드는 추상메서드가 되고
* 이 메서드에는 반드시 오버라이딩 해야 한다.
* 2. 추상 메서드는 상속을 목적으로 선언한 메서드이지 실행을 목적으로
* 선언된 메서드가 아니므로 메서드의 몸체({})부분이 없고
* 선언 마무리도 세미콜론으로 해야 한다.
* 3. 일반 클래스에는 추상 메서드를 선언할 수 없다.
* 추상메서드가 하나 이상 존재하면 무조건 추상클래스로 선언해야 한다.
* 4. 추상클래스 내부에서는 추상메서드가 하나 이상으로 존재한다면 일반 메서드 선언도 가능하다.
*/
public abstract void orderApple();
public abstract void orderOrange();
public abstract void orderGrape();
// 오버라이딩이 필요 없는 메서드
public void fefund() {
System.out.println("제품에 문제가 있어서 환불합니다.");
}
}
package abstract_.abs;
public class Store extends PopupStore{
@Override
public void orderApple() {
System.out.println("착즙사과주스를 20000원에 팝니다");
}
@Override
public void orderOrange() {
System.out.println("착즙오렌지주스를 20000원에 팝니다");
}
@Override
public void orderGrape() {
System.out.println("착즙포도주스를 20000원에 팝니다");
}
}
package abstract_.abs;
public class ConvenientStore extends PopupStore {
@Override
public void orderApple() {
System.out.println("가당 사과주스를 4000원에 팝니다.");
}
@Override
public void orderOrange() {
System.out.println("가당 오렌지주스를 4000원에 팝니다.");
}
@Override
public void orderGrape() {
System.out.println("가당 포도주스를 4000원에 팝니다.");
}
}
package abstract_.abs;
public class MainClass {
public static void main(String[] args) {
// PopupStore 클래스는 직접 객체 생성 불가능
// PopupStore ps = new PopupStore();
PopupStore s = new Store();
// 객체 종류에 따라 실행 구문이 다르게 정의되었지만
// 명세는 같은 메서드
s.orderApple();
s.orderGrape();
s.orderOrange();
// 어떤 객체가 와도 공통적으로 실행되는 메서드
s.refund();
}
}
메서드에 abstract를 붙이면 해당 메서드는 추상메서드가 되고 반드시 오버라이딩 해야 한다.
추상 메서드는 상속을 목적으로 선언한 메서드이지 실행을 목적으로 선언된 메서드가 아니므로 메서드의 몸체({})부분이 없고 선언 마무리도 세미콜론으로 해야 한다.
일반 클래스에는 추상 메서드를 선언할 수 없다.
추상메서드가 하나 이상 존재하면 무조건 추상클래스로 선언해야 한다.
추상클래스 내부에서는 추상메서드가 하나 이상으로 존재한다면 일반 메서드 선언도 가능하다.
* 객체지향적으로 코드를 작성해야 하는 이유
객체 생성 시 Store와 ConvenienvtStore로 타입만 변경만 해주면 아래와 같이 작성된 클라이언트 코드는 변경하지 않아도 된다.
객체 지향적으로 작성하지 않으면 객체가 변경될 때마다 클라이언트 코드도 수정해야 한다.
오버라이딩 된 메서드와 추상메서드는 메서드명이 모두 같지만, abstract 키워드가 없다면 메서드명을 달리 할 수 있기 때문에 객체가 변경될 때마다 클라이언트 코드를 수정하게 된다.
// 클라이언트 코드
// 객체 종류에 따라 실행 구문이 다르게 정의되었지만
// 명세는 같은 메서드
s.orderApple();
s.orderGrape();
s.orderOrange();
// 어떤 객체가 와도 공통적으로 실행되는 메서드
s.refund();
템플릿 메서드
템플릿 메서드 예제
package abstract_.templatemethod;
public abstract class Lottery {
// 템플릿 메서드 패턴은 큰 틀에서 호출구문은 구현메서드(실행문이 있는 메서드)
// 로 정의하고 구현메서드가 호출하는 추상메서드들은 상속 후에 특징을 정하도록
// 만들어서 호출 순서는 그대로 가져갈 수 있도록 하되
// 사용자가 특징만 정의하도록 하는 디자인 패턴이다.
// 구현메서드에는 큰 틀은 같지만 세부사항이 달라질 수 있는 내용을 먼저 작성한다.
public final void lotteryCycle() { // final 이므로 오버라이딩 불가. 순서 고정
// 1. 복권 구매
buyLottery();
// 2. 당첨 여부 확인
checkWinLottery();
// 3. 당첨금 수령
getLotteryMoney();
}
// 세부사항은 상속받은 주체가 무엇인지에 따라 다르게 정의할 수 있도록
// 추상메서드만 정의해놓고 추가적인 작업은 하지 않는다.
abstract void buyLottery();
abstract void checkWinLottery();
abstract void getLotteryMoney();
}
package abstract_.templatemethod;
public class KoreanLotto extends Lottery{
@Override
void buyLottery() {
System.out.println("한 게임에 천원짜리 로또를 삽니다.");
}
@Override
void checkWinLottery() {
System.out.println("45C6의 확률을 뚫고 1등에 당첨되었습니다.");
}
@Override
void getLotteryMoney() {
System.out.println("1등 상금으로 대략 수십억을 받았습니다.");
}
}
package abstract_.templatemethod;
public class StatesSuperball extends Lottery{
@Override
void buyLottery() {
System.out.println("미국가서 슈퍼볼 복권을 삽니다.");
}
@Override
void checkWinLottery() {
System.out.println("69C5 * 26C1 분의 1의 확률로 당첨되었습니다.");
}
@Override
void getLotteryMoney() {
System.out.println("당첨금액은 최소 수천억원입니다.");
}
}
package abstract_.templatemethod;
public class MainClass {
public static void main(String[] args) {
// Lottery lottery = new KoreanLotto();
Lottery lottery = new StatesSuperball();
lottery.lotteryCycle();
}
}
템플릿 메서드인 이유
템플릿처럼 메서드가 정해져있어 정의만하면 되기 때문에 템플릿 메서드라 부른다.
템플릿 메서드의 장점
유지, 보수성이 좋다.
설계한 사람의 의도대로 구현할 수 있다.
인터페이스(Interface)
- 자바의 인터페이스는 객체의 사용 방법을 정의한 타입(메서드 명세서)으로 객체의 교환성을 높여주기 때문에 다형성을 구현하는 매우 중요한 역할을 한다.
- 인터페이스를 선언할 때는 interface 키워드를 사용하며, 클래스에서 인터페이스를 구현할 때는 클래스 이름 뒤에 implements 키워드를 사용하여 구현한다.
- 클래스는 멤버변수, 생성자, 메서드를 구성 멤버로 가지지만 인터페이스는 상수와 메서드만을 구성멤버로 가진다.
- 인터페이스는 데이터를 저장할 수 없기 때문에 데이터를 저장할 객체 또는 정적 변수를 선언할 수 없다.
- 따라서 인터페이스 선언된 변수는 public static final을 생략하더라도 컴파일 과정에서 자동으로 붙게 된다. (상수)
- 인터페이스의 메서드를 추상메서드 형식으로 선언하면 abstract를 붙이지 않더라도 자동으로 컴파일 과정에서 붙게 된다. (추상메서드)
- 클래스가 상속 가능한 것처럼 인터페이스도 extends 키워드를 사용하여 인터페이스 간의 상속을 구현할 수 있으며 다중 상속도 표현할 수 있다.
인터페이스 예제
package interface_;
public interface RemoteController {
// 최대 배터리량, 최소 배터리량을 상수로 지정
int MAX_BATTERY = 100;
int MIN_BATTER = 0;
// 리모콘이 가져야 하는 필수 기능에 대해 정의
public void turnOn();
public void turnOff();
public void showStatus();
}
package interface_;
public class TVRemoteController implements RemoteController {
private final int inch;
private int channel;
public TVRemoteController(int inch) {
this.inch = inch;
this.channel = 1;
}
@Override
public void turnOn() {
System.out.println("TV를 켭니다.");
}
@Override
public void turnOff() {
System.out.println("TV를 끕니다.");
}
@Override
public void showStatus() {
System.out.println("화면크기: " + this.inch);
System.out.println("현재채널: " + this.channel);
}
public void setChanneldown() {
// 1번까지만 채널이 있다.
if(this.channel - 1 < 1) {
this.channel = 1;
} else {
this.channel--;
}
}
public void setChannelup() {
this.channel++;
}
}
package interface_;
public class RobotCleanerRemoteController implements RemoteController {
public String modelName;
public String price;
// 로봇청소기 생성자
public RobotCleanerRemoteController(String modelName, String price) {
this.modelName = modelName;
this.price = price;
}
@Override
public void turnOn() {
System.out.println("로봇청소기를 킨다.");
}
@Override
public void turnOff() {
System.out.println("로봇청소기를 끈다.");
}
@Override
public void showStatus() {
System.out.println("모델명: " + this.modelName);
System.out.println("가격: " + this.price);
}
}
package interface_;
public class MainClass {
public static void main(String[] args) {
// 인터페이스 역시 구현체를 다형성 형식으로 받을 수 있다.
RemoteController rc = new TVRemoteController(50);
// RemoteController rc = new RobotCleanerRemoteController("imou", "28000");
rc.turnOn();
rc.showStatus();
rc.turnOff();
}
}
부록 : has - a 관계
상속의 전제는 is - a 관계다.
그런데 현실에서는 is - a 관계가 아님에도 불구하고 다른 객체의 기능을 쓸 수 있는 경우가 많다.
이때 has - a 관계로 설정해 객체간 사용을 구현하게 된다.
경찰 객체와 총 객체가 있을때 총을 경찰이 발사하는 상황을 구현한다면
경찰이 멤버변수로 총을 보유하고 있는 상황을 구현해야지,
경찰이 총의 기능을 상속해서는 안된다는 뜻이다.
예제
package has_a;
public class Gun {
private int bullet; // 총알 갯수
private String modelName; // 총기 모델명
private String gunNumber; // 총번
public Gun(String modelName, String gunNumber) {
this.bullet = 5;
this.modelName = modelName;
this.gunNumber = gunNumber;
}
public void shoot() {
if(this.bullet > 0) {
this.bullet--;
System.out.println("총을 쐈습니다.");
} else {
System.out.println("방아쇠를 당겼지만 총알이 없습니다.");
}
}
public void reload() {
this.bullet = 5;
}
}
package has_a;
public class Police {
// 상속 없이 Gun 기능을 사용하기 위해 멤버변수 Gun도 가진다.
private Gun gun;
private String name;
private int height;
public Police(Gun gun, String name, int height) {
this.gun = gun;
this.name = name;
this.height = height;
}
public void shoot() {
this.gun.shoot();
}
public void showStatus() {
System.out.println("소유 총기: " + this.gun);
System.out.println("이름: " + this.name);
System.out.println("키 : " + this.height);
}
}
package has_a;
public class MainClass {
public static void main(String[] args) {
// Gun을 new 키워드로 생성해야 Police 생성자에 전달 가능
Gun gun = new Gun("M-16", "369486");
// gun을 사전에 생성하지 않으면 넘길 방법이 없다.
Police police = new Police(gun, "나경찰", 180);
police.shoot();
police.shoot();
police.shoot();
police.shoot();
police.shoot();
police.shoot();
police.shoot();
police.shoot();
police.shoot();
}
}
'네트워크 캠퍼스 > JAVA' 카테고리의 다른 글
240118 자바 예외처리(throw, 사용자 정의 예외, 자바의 예외처리 전략), 자바 API(java.lang, Object, System) (0) | 2024.01.18 |
---|---|
240117 자바의 예외처리(Exception, Runtime Exception, try~catch, 다중 catch, throws) (0) | 2024.01.17 |
240115 싱글톤 패턴, final(class/변수), 상수, abstract (0) | 2024.01.15 |
240112 사용 제한자(static 변수와 메서드) (0) | 2024.01.12 |
240111 매개 변수의 다형성, 강제 타입 변환 (0) | 2024.01.12 |