인터페이스 (Interface)
목차
📍 인터페이스 (Interface)
1–1. 인터페이스를 상속 받아 구현하는 서브클래스 정의
📍 인터페이스 (Interface)
– 클래스가 아니므로 선언 시 calss 키워드를 사용하지 않고 interface 키워드를 사용하여 정의.
– 인터페이스는 상수와 추상메서드만 가질 수 있음.
– 모든 멤버변수는 public static final이 붙은 상수로 취급됨.
→ public static final 생략 가능.
→ final로 인해 값 변경 불가.
→ static으로 인해 클래스명만으로 접근 가능.
→ public으로 인해 누구나 접근 가능.
– 모든 메서드는 public abstract가 붙은 추상메서드로 취급됨.
→ public abstract 생략 가능.
→ 메서드 바디를 가질 수 없으며, public으로 인해 누구나 접근 가능.
– 추상클래스와 마찬가지로 추상메서드를 포함하므로 객체 생성 불가능.
→ 단, 참조변수 타입으로는 사용 가능 (= 다형성을 활용한 업캐스팅 가능)
– 서브클래스에서 인터페이스를 상속받아 구현해야 하는 경우, extends 대신 implements 키워드를 사용함.
→ 클래스는 상속 받아 확장 (extends)의 개념이 강하지만,
인터페이스는 상속 받아 추상메서드를 구현 (implement)의 개념이 강함.
– 클래스는 서브클래스에서 다중 상속이 불가능하지만, 인터페이스는 서브클래스에서 다중 상속 (구현)이 가능함.
→ implements 키워드 뒤에 복수개의 인터페이스 지정 가능.
– 인터페이스끼리 상속받을 경우, implements가 아닌 extends로 상속.
→ 인터페이스는 추상메서드만 가질 수 있으므로
인터페이스끼리 상속받으면 추상메서드를 구현(implements)하지 못하기 때문.
▸ 인터페이스 정의 기본 문법
[접근제한자] interface 인터페이스명 {
상수
추상메서드
}
▸ 인터페이스를 상속 받은 서브클래스 정의 문법
[접근제한자] class 클래스명 implements 인터페이스명 { }
– 인터페이스를 상속 받은 (= 구현 하는) 서브클래스를 '구현클래스' 라고도 함.
→ 인터페이스의 추상메서드를 서브클래스에서 반드시 구현해야 하기 때문. (오버라이딩의 강제성)
EX ) |
• 인터페이스 정의
– 클래스 안에 인터페이스 정의 가능.
interface MyExInterface {
// 상수
public static final int NUM1 = 10;
int NUM2 = 20;
// int NUM3;
// 생성자 X
// public MyExInterface() {}
// 추상메서드
public abstract void method1();
// public void method2() {}
public void method2();
void method3();
}
코드 분석
4~6 | – 인터페이스 내의 모든 멤버변수는 상수. – public static final 생략 가능. – The blank final field NUM2 may not have been initialized 에러 발생 → 상수는 초기화 값을 줘야 하므로 에러 발생. |
9 | – Interfaces cannot have constructors 에러 발생. → 인터페이스는 생성자를 가질 수 없음. |
13 | – 일반 메서드 사용 불가. Abstract methods do not specify a body 에러 발생. → 인터페이스 내의 메서드는 추상메서드여야 하므로 바디를 가질 수 없다고 에러 발생. |
14, 15 | – 추상메서드 'abstract' 생략 가능. – 'public abstract' 생략 가능. |
• 인터페이스를 상속 받아 구현하는 서브클래스 (구현클래스) MySubClass 정의
– 서브클래스 정의 시 클래스명 뒤에 implements 키워드를 쓰고 인터페이스명 지정.
– 인터페이스는 추상메서드만 가질 수 있으므로 서브클래스에서 반드시 오버라이딩 해야 함.
class MySubClass implements MyExInterface{
@Override
public void method1() {
System.out.println("서브클래스에서 구현한 추상메서드 method1()");
}
@Override
public void method2() {
System.out.println("서브클래스에서 구현한 추상메서드 method2()");
}
@Override
public void method3() {
System.out.println("서브클래스에서 구현한 추상메서드 method3()");
}
}
– 인터페이스에서 method3() 정의 시 접근제한자를 생략했지만,
인터페이스 내의 모든 메서드는 public 접근제한을 가지므로,
오버라이딩을 통한 구현 시 무조건 public 접근제한자만 사용 가능.
→ 오버라이딩 메서드의 접근제한자는 같거나 넓은 범위로만 변경 가능, 좁아질 수 없음.
• 추상클래스 정의
– 추상클래스와 인터페이스의 차이
abstract class MyClass{
public static final int NUM1 = 10; // 상수
public int NUM2 = 20; // 인스턴스 멤버변수 O
// 생성자
public MyClass() {}
// 추상메서드
public abstract void method1();
// 일반메서드
public void method2() {}
}
– 추상클래스 내의 모든 멤버변수는 상수가 아니며, 선언 방법에 따라 달라짐.
– 추상클래스는 생성자와 일반메서드도 가질 수 있음.
– 추상메서드를 갖는 클래스는 반드시 추상클래스로 선언해야 함.
• 인스턴스 생성
// MyClass mc = new MyClass();
// MyExInterface mi = new MyExInterface();
MySubClass msc = new MySubClass();
msc.method1();
msc.method2();
msc.method3();
코드 분석
1 | – 추상클래스는 인스턴스 생성이 불가능. → 추상메서드의 바디가 구현되지 않았으므로 호출이 불가한데 인스턴스를 생성하면 호출 할 수 있기 때문. |
2 | – 인터페이스도 인스턴스 생성이 불가능. → 추상메서드때문에 추상클래스의 인스턴스 생성이 불가능하고, 인터페이스는 추상메서드만 가질 수 있으므로 마찬가지로 인스턴스 생성 불가능. |
4~7 | – 인터페이스를 구현한 서브클래스 (구현클래스) 인스턴스 생성 가능. |
✓ 실행 결과
서브클래스에서 구현한 추상메서드 method1()
서브클래스에서 구현한 추상메서드 method2()
서브클래스에서 구현한 추상메서드 method3()
• 인터페이스 내의 상수
// MyExInterface.NUM1 = 99;
// MySubClass.NUM1 = 99;
// msc.NUM1 = 99;
– 인터페이스 내의 모든 변수는 상수이므로 값 변경 불가.
→ 구현체 클래스 (서브클래스)도 동일한 상수가 상속되므로 값 변경 불가함.
• 업캐스팅
MyExInterface mi2 = new MySubClass();
mi2.method1();
mi2.method2();
mi2.method3();
System.out.println(mi2.NUM1);
System.out.println(mi2.NUM2);
코드 분석
1 | – 인터페이스는 인스턴스 생성은 불가능하지만, 참조변수 타입으로 사용은 가능. → 구현체 클래스 (서브클래스)의 인스턴스를 인터페이스 타입으로 업캐스팅 가능. → 다형성 활용 가능. |
3~5 | – 업캐스팅 후에는 참조 영역이 축소되므로 상속된 메서드만 호출 가능. → 인터페이스의 추상메서드를 모두 오버라이딩 했기 때문에, 인터페이스가 가진 모든 메서드는 참조 영역 축소 후에도 호출 가능. |
7, 8 | – 상수는 인터페이스에서 선언했으므로 업캐스팅 후에도 당연히 접근 가능. |
✓ 실행 결과
서브클래스에서 구현한 추상메서드 method1()
서브클래스에서 구현한 추상메서드 method2()
서브클래스에서 구현한 추상메서드 method3()
10
20
▶ 클래스의 다중 상속 문제점
– Java에서는 클래스에서 다중 상속이 일어날 경우 발생하는 문제점이 있으므로 단일 상속만 허용하고 있음.
→ 해당 클래스들의 관계를 그리면 마치 다이아몬드 모양과 같아 다이아몬드 상속에서의 문제점 (이슈)라고도 함.
EX ) |
• 동물 추상클래스 정의
abstract class 동물{
public abstract void 번식();
}
• 상어 클래스 정의 — 추상클래스 동물 상속
class 상어 extends 동물{
@Override
public void 번식() {
System.out.println("알을 낳아 번식");
}
}
• 고래 클래스 정의 — 추상클래스 동물 상속
class 고래 extends 동물{
@Override
public void 번식() {
System.out.println("새끼를 낳아 번식");
}
}
– 번식() 메서드를 갖는 슈퍼클래스 동물 클래스를 정의하고, 고래와 상어클래스에서 동물 클래스를 상속 받기.
• 고래상어 클래스 정의 — 고래와 상어 클래스 상속
class 고래상어 extends 고래, 상어{
}
– 외부 또는 내부에서 고래와 상어클래스가 공통적으로 갖는 번식() 메서드를 호출하게 되면,
어느 객체의 번식() 메서드를 호출하는지 분명하지 않기 때문에 Java에서는 다중 상속이 금지되어 있음.
▶ 인터페이스의 다중 상속
– 클래스는 서브클래스에서 다중 상속이 불가능하지만, 인터페이스는 서브클래스에서 다중 상속 (구현)이 가능함.
→ implements 키워드 뒤에 복수개의 인터페이스 지정 가능.
EX ) |
• '동물' 인터페이스 정의
interface 동물{
public abstract void 번식();
}
– 고래와 상어의 특징을 추상화하여 동물 인터페이스로 정의.
– 인터페이스 내의 모든 메서드는 추상메서드.
• '헤엄칠수있는' 인터페이스 정의
interface 헤엄칠수있는 {}
– 헤엄칠 수 있는 특징을 갖는 인터페이스 정의.
• '동물' 인터페이스를 상속받는 서브인터페이스 '고래'와 '상어' 정의
interface 고래 extends 동물, 헤엄칠수있는{}
interface 상어 extends 동물, 헤엄칠수있는{}
– 인터페이스끼리 상속은 implements가 아닌 extends 사용.
– 인터페이스끼리 상속은 다중 상속도 가능.
• '고래'와 '상어' 인터페이스를 동시에 상속 받아 구현하는 서브클래스 '고래상어' 정의
class 고래상어 implements 고래, 상어{
@Override
public void 번식() {
System.out.println("알을 낳아 번식");
}
}
– 클래스에서 인터페이스를 상속 받을 경우 implements (구현)키워드를 사용하며,
반드시 인터페이스가 가진 추상메서드를 오버라이딩하여 구현해야 함.
→ 또한, 여러개의 인터페이스를 상속받을 수 있음.
– 슈퍼클래스 (인터페이스)의 모든 메서드가 추상메서드이므로,
중복되는 메서드에 대한 구별없이 직접 구현하면 되기 때문에 중복된 메서드로 인한 혼란이 없음.
→ 다이아몬드 상속에서의 문제점이 사라짐.
• main 메서드에서 실행
고래상어 고래상어 = new 고래상어();
고래상어.번식();
// 업캐스팅
동물 동물 = 고래상어;
동물.번식();
– 원래 업캐스팅 후에는 참조 영역이 축소되어 상속된 메서드만 호출 가능하지만,
인터페이스가 가진 모든 메서드는 참조 영역 축소 후에도 호출이 가능함.
→ 인터페이스의 추상메서드를 모두 서브클래스에서 오버라이딩 했기 때문.
→ 서브클래스에서 오버라이딩 된 메서드가 출력됨.
✓ 실행 결과
알을 낳아 번식
알을 낳아 번식
▶ 인터페이스의 필요성
1. 구현의 강제로 코드의 통일성 향상 (= 표준화)
2. 인터페이스를 통한 간접적인 클래스 사용으로 모듈 교체가 용이함.
→ 특정 클래스를 직접 다루는 대신 부모 인터페이스 타입으로 클래스를 다루게 되면,
실제 인스턴스가 바뀌더라도 기존 코드를 수정할 필요가 없어짐.
3. 서로 상속 관계가 없는 클래스간의 인터페이스를 통한 상속 관계 부여.
→ 다형성 확장.
4. 모듈간 독립적 프로그래밍으로 인한 개발 기간 단축.
– 부품을 갈아끼움 = 모듈 교체
– 공통부분을 인터페이스로 만들어서 공유가능.
– extens는 상속할 때 한 번만 사용 가능하며 implements (구현)는 여러번 가능함.
▸ 인터페이스를 통한 간접적인 클래스 사용
– 특정 클래스를 직접 다루는 대신 부모 인터페이스 타입으로 클래스를 다루게 되면,
실제 인스턴스가 바뀌더라도 기존 코드를 수정할 필요가 없어짐.
EX ) |
• 문서 등을 프린터로 출력하기 위한 각 프린터 클래스 정의
– 각 프린터의 출력 기능을 갖는 Printer 인터페이스를 정의하고, 각 프린터 클래스에서 상속받아 구현.
1. Printer 인터페이스
interface Printer{
public abstract void print(String fileName);
}
– 프린터기의 공통 기능인 출력 (print())기능을 추상메서드로 정의함.
2. 레이저 프린터 — Printer 인터페이스 상속
class LaserPrinter implements Printer{
@Override
public void print(String fileName) {
System.out.println("Laser Printer로 " + fileName + " 출력하기");
}
}
– 파일 (String타입 fileName)을 전달받아 출력 작업을 수행하는 print() 메서드 오버라이딩.
→ Printer 인터페이스로부터 상속받아 구현.
3. 잉크젯 프린터 — Printer 인터페이스 상속
class InkjetPrinter implements Printer{
@Override
public void print(String fileName) {
System.out.println("InkjetPrinter로 " + fileName + " 출력하기");
}
}
– 파일 (String타입 fileName)을 전달받아 출력 작업을 수행하는 print() 메서드 오버라이딩.
→ Printer 인터페이스로부터 상속받아 구현.
4. 도트 프린터 — Printer 인터페이스 상속
class DotPrinter implements Printer{
@Override
public void print(String fileName) {
System.out.println("DotPrinter로 " + fileName + " 출력하기");
}
}
– 파일 (String타입 fileName)을 전달받아 출력 작업을 수행하는 print() 메서드 오버라이딩.
→ Printer 인터페이스로부터 상속받아 구현.
• main 메서드에서 실행
// 인스턴스 생성
LaserPrinter lp = new LaserPrinter();
lp.print("lp.java");
InkjetPrinter ip = new InkjetPrinter();
ip.print("ip.java");
DotPrinter dp = new DotPrinter();
dp.print("dp.java");
// 업캐스팅
Printer p = new LaserPrinter();
p.print("하이");
p = new InkjetPrinter();
p.print("바이");
– 인터페이스 사용 시, 손쉬운 모듈 교체를 지원함.
✓ 실행 결과
Laser Printer로 lp.java 출력하기
InkjetPrinter로 ip.java 출력하기
DotPrinter로 dp.java 출력하기
Laser Printer로 하이 출력하기
InkjetPrinter로 바이 출력하기
• 인터페이스를 통한 간접적인 클래스 사용
– 각 프린터를 직접 다루지 않고,
상위 타입 (부모)인 Printer 인터페이스를 다루는 PrinterClient 클래스를 정의함.
– 마치 부품을 갈아 끼우는 것과 같음 (= 모듈 교체)
class PrinterClient{
// Printer 인터페이스 타입 변수 선언
private Printer printer;
// Setter 메서드 정의
public void setPrinter(Printer printer) {
this.printer = printer;
}
// print 메서드 정의
public void print(String fileName) {
printer.print(fileName);
}
}
코드 분석
4 | – 각각의 프린터 클래스를 다루기 위한 슈퍼클래스 타입에 해당하는 Printer 인터페이스 타입 변수 선언. |
7~9 | – Setter 메서드를 통한 Printer 타입 변수 초기화. |
12~14 | – 외부로부터 출력할 파일을 전달받아, 실제 프린터에 해당하는 각 인스턴스의 print() 메서드를 호출한 뒤 파일을 전달하여 출력 작업을 수행. – Printer 타입 변수에 저장된 각 프린터의 인스턴스를 통해, print() 메서드를 호출하면 해당 프린터의 출력 기능을 사용 가능함. |
• main 메서드에서 실행
PrinterClient pc = new PrinterClient();
pc.setPrinter(new LaserPrinter());
pc.print("Hello.java");
pc.setPrinter(new InkjetPrinter());
pc.print("Hello.java");
pc.setPrinter(new DotPrinter());
pc.print("Hello.java");
코드 분석
3 | – PrintClient 인스턴스의 setPrinter() 메서드를 호출하여 각 프린터기 인스턴스 파라미터로 전달하면 업캐스팅 일어남. → 부품을 LaserPrinter로 바꿈. |
4 | – LaserPrinter → Printer 업캐스팅 → PrinterCilent 인스턴스의 print() 메서드를 호출하면 인스턴스 내의 Printer 타입 변수에 저장된 인스턴스의 print() 호출됨. → 결국 실제 저장된 LaserPrinter 인스턴스의 print() 메서드가 호출됨. |
6, 7 | – InkjetPrinter → Printer 업캐스팅 – 위의 LaserPrintr를 InkjetPrinter 로 교체 시, setPrinter() 메서드에서 InkjetPrinter 인스턴스만 전달하면 자동으로 출력 대상이 변경됨. |
9, 10 | – DotPrinter → Printer 업캐스팅 |
✓ 실행 결과
Laser Printer로 Hello.java 출력하기
InkjetPrinter로 Hello.java 출력하기
DotPrinter로 Hello.java 출력하기
▸ 상속 관계가 없는 클래스간의 상속 관계 부여
– 인터페이스를 통한 상속 관계를 부여하면 다형성 확장이 일어남.
– 사실상 다형성 확장때문에 인터페이스를 많이 사용함.
EX ) |
• 인터페이스 정의하지 않았을 경우
class Pc{}
class NoteBookPc extends Pc{
public void charge() {
System.out.println("노트북 충전 중...");
}
}
class HandPhone{}
class SmartPhone extends HandPhone{
public void charge() {
System.out.println("스마트폰 충전 중...");
}
}
– 서로 다른 class를 상속받는 NoteBookPc와 SmartPhone은 메서드명이 같은 charge() 메서드를 가지고 있음.
• 인터페이스 정의했을 경우
– Object 클래스 외에는 공통 슈퍼클래스가 없는 NoteBookPc와 SamrtPhone의 공통 인터페이스 Chargeable 정의.
interface Chargeable{
public abstract void charge();
}
class NoteBookPc2 extends Pc implements Chargeable{
@Override
public void charge() {
System.out.println("노트북 충전 중...");
}
}
class SmartPhone2 extends HandPhone implements Chargeable{
@Override
public void charge() {
System.out.println("스마트폰 충전 중...");
}
}
코드 분석
1~3 | – 두 클래스에서 공통적으로 사용할 충전 기능 charge() 메서드를 추상메서드로 정의. |
6~18 | – 기존에 Pc 클래스를 상속받고 있는 상태에서 추가로 인터페이스를 구현해야하는 경우, 상속 코드 뒤에 구현 코드를 기술. → implements Chargeable 코드 추가. → 아무 관계 없던 두 클래스에 동일한 부모 인터페이스가 추가되어 서로 상속 관계에 묶이게 됨. → 기존에 중복되었던 charge() 메서드를 각 클래스에서 오버라이딩 하여 사용함. ⇒ 두 클래스의 공통적인 특징을 인터페이스로 추가해주는 것. |
• noRelationShip() 메서드 정의
– 인터페이스를 통해 공통된 멤버를 갖지 않는 경우 (NoteBookPc & SmartPhone 클래스)
– main 메서드 아래와 본문 클래스 윗 부분에 정의함.
} // main 메서드 끝
public void noRelationShip() {
// NoteBookPc notebook = new NoteBookPc();
// notebook.charge();
// SmartPhone smartphone = new SmartPhone();
// smartphone.charge();
// 두 개의 인스턴스를 하나의 배열로 관리 (업캐스팅)
Object[] objArr = {new NoteBookPc(), new SmartPhone()};
// 반복문을 사용하여 배열 크기만큼 반복
for(int i=0; i<objArr.length; i++) {
// objArr[i].charge();
// Object타입으로 charge()메서드 호출 불가 (참조 영역 축소)
// instanceof 연산자
if(objArr[i] instanceof NoteBookPc) {
NoteBookPc notebook = (NoteBookPc) objArr[i];
notebook.charge();
}else if(objArr[i] instanceof SmartPhone) {
SmartPhone smartphone = (SmartPhone) objArr[i];
smartphone.charge();
}
}
} // noRelationShip() 메서드 끝
코드 분석
5~8 | – 원래는 이렇게 인스턴스를 생성해서 참조변수.메서드로 메서드 호출했음. 아래의 하나의 배열로 호출하는 부분과 중복되므로 주석 처리 함. |
11 | – 두 개의 인스턴스를 하나의 배열로 관리해야 할 경우, NoteBookPc와 SamrtPhone의 공통 타입은 Object타입 밖에 없음. → Object타입으로 업캐스팅. |
15 | – The method charge() is undefined for the type Object 에러 발생. – 참조 영역의 축소로 인해 Object 타입으로 charge() 메서드 호출 불가. → 다운캐스팅을 통해 각 인스턴스를 따로 접근해야 함. → 안전한 다운캐스팅을 위해 instanceof 연산자를 사용, NoteBookPc와 SmartPhone타입 판별. |
20~22 | – Object → NoteBookPc타입으로 다운캐스팅 후 charge() 호출 가능. → 노트북 충전기로 노트북 충전함. |
23~25 | – Object → SmartPhone타입으로 다운캐스팅 후 charge() 호출 가능. → 스마트폰 충전기로 스마트폰 충전함. |
• hasRelationShip() 메서드 정의
– main 메서드 아래와 본문 클래스 윗 부분에 정의함.
– 인터페이스를 통해 공통된 멤버를 갖는 상속 관계를 부여할 경우 (NoteBookPc2 & SmartPhone2 클래스)
해당 인터페이스 타입으로 다운캐스팅 할 필요 없이 업캐스팅 된 상태 그대로 멤버에 접근 가능.
→ 다형성으로 인한 코드 절약.
public void hasRelationShip() {
// 방법1) 참조변수 2개로 관리
// → 업캐스팅
// Chargeable c = new NoteBookPc2();
// Chargeable c2 = new SmartPhone2();
// 방법2) 배열 하나로 깔끔하게 관리
// → 한 번에 둘 다 업캐스팅
Chargeable[] chargeableArr = {new NoteBookPc2(), new SmartPhone2()};
// 다운캐스팅 없이 바로 메서드 호출 가능
for(int i=0; i<chargeableArr.length; i++) {
if(chargeableArr[i] instanceof NoteBookPc2) {
chargeableArr[i].charge();
}else if(chargeableArr[i] instanceof SmartPhone2) {
chargeableArr[i].charge();
}
}
}
} // Ex4 클래스 끝
코드 분석
5, 6 | – 방법1) 참조변수 2개로 관리 → NoteBookPc2 → Chargeable 업캐스팅, SmartPhone → Chargeable 업캐스팅 |
10 | – 방법2) 배열 하나로 모두 관리 → 배열 데이터 입력하는 곳에 두 인스턴스를 생성하여 한 번에 업캐스팅 함. |
13~19 | – 업캐스팅 후에도 공통 메서드 charge()를 호출 가능하므로 별도의 다운캐스팅 없이 바로 접근 가능. – instanceof 연산자를 사용하여 배열의 인덱스 i번째에 있는 인스턴스가 무엇인지에 따라 해당 클래스에서 chargeableArr 인터페이스의 추상메서드를 오버라이딩 한 charge() 메서드 호출됨. (NoteBookPc2 클래스와 SmartPhone2 클래스는 Chargeable 인터페이스를 구현한 클래스.) |
• main 메서드에서 실행하기
Ex4 ex4 = new Ex4();
ex4.noRelationShip();
System.out.println("------------------------");
ex4.hasRelationShip();
– 아래의 noRelationShip(), hasRelationShip() 메서드는 static 메서드가 아니므로
Ex4 클래스의 인스턴스를 생성해야만 참조변수명.메서드명() 으로 접근 가능.
– 만약 static 메서드로 정의했다면, 클래스명.메서드명() 으로 바로 호출 가능.
✓ 실행 결과
노트북 충전 중...
스마트폰 충전 중...
------------------------
노트북 충전 중...
스마트폰 충전 중...
▸ 모듈간 독립적 프로그래밍으로 인한 개발 기간 단축
EX ) |
• 개발자와 디자이너의 '공통 부분 (= 기능)' 을 인터페이스로 정의.
interface LoginProcess{
public abstract String login(String id, String pass);
}
– 공통기능으로 login() 추상메서드를 정의하여 파라미터와 리턴타입을 지정함.
1. 디자이너의 경우
1 ) LoginProcess 인터페이스를 구현하는 Designer 클래스 정의
– 로그인 처리 과정은 중요하지 않고 전달 데이터와 리턴 데이터만 중요함.
– LoginProcess 인터페이스를 구현하는 클래스를 정의하여 login() 메서드를 구현.
class Designer implements LoginProcess{
@Override
public String login(String id, String pass) {
System.out.println("디자이너가 전달받은 아이디 : "+ id);
System.out.println("디자이너가 전달받은 패스워드 : "+ pass);
return "성공";
}
}
– 메서드에 전달되는 데이터가 정확한지, 리턴값이 외부로 잘 전달되는지만 확인하면 됨.
2 ) DesignerClient 클래스 정의
class DesignerClient{
// 인스턴스 생성
Designer designer = new Designer();
// login() 메서드
public void login() {
String id = "admin";
String pass = "1234";
String result = designer.login(id, pass);
System.out.println("로그인 결과 : " + result);
if(result.equals("성공")) {
// 로그인 성공 페이지로 이동
}else {
// 로그인 실패 페이지로 이동
}
}
}
– Designer 클래스의 인스턴스를 생성하고 login() 메서드를 호출하여 바로 위의 변수 아이디, 패스워드를 전달하면,
해당 메서드의 리턴값이 변수 result에 저장됨.
→ 로그인 결과로 리턴되는 값에 따라 확인 작업 수행.
– '성공'이라는 데이터를 리턴값으로 받았다면 조건문은 '참'이 되어 if문 실행.
→ 로그인 성공 페이지로 이동 처리 작업 수행.
→ '성공' 데이터가 아니라면 로그인 실패 페이지로 이동 처리 작업 수행.
• 개발자의 경우
– 전달받은 값은 중요하지 않고 전달받은 값을 사용하여 로그인 처리 작업 수행.
→ 로그인 처리 후 리턴되는 값이 정상적으로 전달되는지만 확인.
– LoginProcess 인터페이스를 구현하는 Developer 클래스 정의.
1 ) LoginProcess 인터페이스를 구현하는 Developer 클래스 정의
class Developer implements LoginProcess{
@Override
public String login(String id, String pass) {
System.out.println("개발자가 전달받은 아이디 : " + id);
System.out.println("개발자가 전달받은 패스워드 : " + pass);
System.out.println("아아디와 패스워드로 로그인 작업 처리 완료!");
return "성공";
}
}
– 디자이너로부터 전달받은 아이디와 패스워드를 사용하여 로그인 작업을 처리한 후, '성공' 또는 '실패' 문자열 리턴.
2 ) DeveloperClinet 클래스 정의
class DeveloperClient{
// 인스턴스 생성
Developer developer = new Developer();
// login() 메서드
public void login() {
String result = developer.login("admin", "1234");
System.out.println("로그인 결과 : " + result);
}
}
– 디자이너가 전달하게 될 아이디와 패스워드를 임의로 설정하여 전달, 개발자 코드를 통해 로그인 작업을 수행한 후
리턴되어지는 값이 정상적인지만 확인하면 됨.
• main 메서드에서 실행하기
// 디자이너의 경우
DesignerClient designer = new DesignerClient();
designer.login();
System.out.println("--------------------------");
// 개발자의 경우
DeveloperClient developer = new DeveloperClient();
developer.login();
✓ 실행 결과
디자이너가 전달받은 아이디 : admin
디자이너가 전달받은 패스워드 : 1234
로그인 결과 : 성공
--------------------------
개발자가 전달받은 아이디 : admin
개발자가 전달받은 패스워드 : 1234
아아디와 패스워드로 로그인 작업 처리 완료!
로그인 결과 : 성공