728x90
반응형
Contents
인터페이스의 필요성
구현의 강제로 코드의 통일성 향상(= 표준화)
인터페이스를 통한 간접적인 클래스 사용으로 모듈 교체가 용이
=> 부모 인터페이스 타입으로 클래스를 다루게 되면 실제 인스턴스가 바뀌더라도 기존 코드를 수정할 필요가 없어짐
서로 상속 관계가 없는 클래스간의 인터페이스를 통한 상속 관계 부여
=> 다형성 확장
모듈간 독립적 프로그래밍으로 인한 개발 기간 단축
인터페이스를 통한 간접적인 클래스 사용으로 모듈 교체가 용이 => 인터페이스 사용 시 손쉬운 모듈 교체를 지원한다!
문제 1
PrinterClient pc = new PrinterClient();
// PrinterClient 인스턴스의 setPrinter() 메서드를 호출하여
// 각 프린터기 인스턴스를 파라미터로 전달하면 업캐스팅 일어남
pc.setPrinter(new LaserPrinter()); // LaserPrinter -> Printer 업캐스팅
// => setPrinter(Printer printer)로 정의되어 있으므로
// Printer printer = new LaserPrint(); 가 됨
// 좌변의 타입이 부모클래스 타입이고, 오른쪽의 인스턴스 생성이 자식클래스이라면
// 묵시적 형변환 즉, 업캐스팅이 일어남!
pc.print("Hello.java");
// => PrinterClient 인스턴스의 print() 메서드를 호출하면
// 인스턴스 내의 Printer 타입 변수에 저장된 인스턴스의 print() 호출됨
// => 결국 실제 저장된 LaserPrinter 인스턴스의 print() 메서드가 호출됨
// 현재 LaserPrinter 를 InkjetPrinter 로 교체 시
// setPrinter() 메서드에 InkjetPrinter 인스턴스만 전달하면
// 자동으로 출력 대상이 변경됨
pc.setPrinter(new InkjetPrinter()); // InkjetPrinter -> Printer 업캐스팅
pc.print("Hello.java");
pc.setPrinter(new DotPrinter()); // DotPrinter -> Printer 업캐스팅
pc.print("Ex3.java");
}
}
// 각 프린터를 직접 다루지 않고 상위 타입인 Printer 인터페이스를 다루는
// PrinterClient 클래스 정의
class PrinterClient {
// 각각의 프린터 클래스를 다루기 위한 슈퍼클래스 타입에 해당하는
// Printer 인터페이스 타입 변수 선언
private Printer printer;
// Setter 메서드를 통한 Printer 타입 변수 초기화
public void setPrinter(Printer printer) {
this.printer = printer;
}
// 외부로부터 출력할 파일을 전달받아 실제 프린터에 해당하는 각 인스턴스의
// print() 메서드를 호출한 뒤 파일을 전달하여 출력 작업을 수행
public void print(String fileName) {
// Printer 타입 변수에 저장된 각 프린터의 인스턴스를 통해
// print() 메서드를 호출하면 해당 프린터의 출력 기능을 사용 가능함
printer.print(fileName);
}
}
// --------------------------------------------------------------------
/*
* 문서 등을 프린터로 출력하기 위한 각 프린터 클래스 정의
* => 각 프린터의 출력 기능을 갖는 printer 인터페이스를 정의하고
* 각 프린터 클래스에서 상속받아 구현
*/
interface Printer { // 프린터
// 프린터기의 공통 기능인 출력(print()) 기능을 추상메서드 정의
public abstract void print(String fileName);
}
class LaserPrinter implements Printer { // 레이저 프린터
// 파일(String 타입 fileName) 을 전달받아 출력 작업을 수행하는 print() 메서드 정의
// => Printer 인터페이스로부터 상속받아 구현
@Override
public void print(String fileName) {
System.out.println("Laser Printer 로 " + fileName + " 출력하기!");
}
}
class InkjetPrinter implements Printer { // 잉크젯 프린터
@Override
public void print(String fileName) {
System.out.println("Inkjet Printer 로 " + fileName + " 출력하기!");
}
}
class DotPrinter implements Printer { // 도트 프린터
@Override
public void print(String fileName) {
System.out.println("Dot Printer 로 " + fileName + " 출력하기!");
}
}
문제2
package interface_;
public class Ex4 {
public static void main(String[] args) {
// 3. 서로 상속 관계가 없는 클래스간에 인터페이스를 통한 상속 관계 부여
// => 다형성 확장
Ex4 ex = new Ex4();
ex.noRelationShip();
System.out.println("---------------");
ex.hasRelationShip();
} // main() 메서드 끝
public void noRelationShip() {
// NoteBookPc notebook = new NoteBookPc();
// notebook.charge();
// SmartPhone smartPhone = new SmartPhone();
// smartPhone.charge();
// 두 개의 인스턴스를 하나의 배열로 관리해야 할 경우
// NoteBookPc 와 SmartPhone 의 공통 타입은 Object 타입밖에 없음
Object[] objArr = {new NoteBookPc(), new SmartPhone()};
// 반복문을 사용하여 배열 크기만큼 반복
for(int i = 0; i < objArr.length; i++) {
// objArr[i].charge();
// => 참조 영역 축소로 인해 Object 타입으로 charge() 메서드 호출 불가!
// instanceof 연산자를 사용하여 NoteBookPc 와 SmartPhone 타입 판별
// => 다운캐스팅을 통해 각 인스턴스를 따로 접근해야 함!
// 접근하고자 하는 charge() 메서드는 각 클래스에서 직접 정의한
// 인스턴스 멤버 메서드이기 때문!
if(objArr[i] instanceof NoteBookPc) {
// Object -> NoteBookPc 타입으로 다운캐스팅 후 charge() 호출
NoteBookPc notebook = (NoteBookPc)objArr[i];
notebook.charge(); // 노트북의 충전기를 통해 충전
} else if(objArr[i] instanceof SmartPhone) {
// Object -> SmartPhone 타읍으로 다운캐스팅 후 charge() 호출
SmartPhone smartPhone = (SmartPhone)objArr[i];
smartPhone.charge(); // 스마트폰의 충전기를 통해 춘전
}
}
}
public void hasRelationShip() {
// 인터페이스를 사용하여 공통된 멤버를 갖는 상속 관계를 부여할 경우
// 해당 인터페이스 타입으로 다운캐스팅 할 필요없이
// 업캐스팅 된 상태 그대로 멤버에 접근 가능
// => 다형성으로 인한 코드 절약
Chargeable c = new NoteBookPc2(); // NoteBookPc2 -> Chargeable
Chargeable c2 = new SmartPhone2(); // SmartPhone2 -> Chargeable
// Chargeable 타입 배열로 두 클래스 인스턴스 모두 관리 가능(업캐스팅)
Chargeable[] chageableArr = {new NoteBookPc2(), new SmartPhone2()};
// 업캐스팅 후에도 공통 메서드 charge() 를 호출 가능하므로
// 별도의 다운캐스팅 없이 바로 charge() 메서드 접근 가능
for(int i = 0; i < chageableArr.length; i++) {
chageableArr[i].charge();
}
}
} // Ex4 클래스 끝
// Object 클래스 외에 슈퍼클래스가 없는 NoteBookPc 와 SmartPhone 의
// 공통 인터페이스 Chargeable 인터페이스 정의
interface Chargeable {
// 두 클래스에서 공통으로 사용할 충전(charge()) 기능을 추상메서드로 정의
public abstract void charge();
}
// 기존에 Pc 클래스를 상속받고 있는 상태에서
// 추가로 인터페이스를 구현해야하는 경우 상속 코드 뒤에 구현 코드를 기술
// => implements Chargeable 코드 추가
// => 아무 관계도 없던 두 클래스에 동일한 부모 인터페이스가 추가되어
// 서로 상속 관계로 묶이게 됨
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("스마트폰 충전 중...");
}
}
// ===============================================================
class Pc {}
class NoteBookPc extends Pc {
public void charge() {
System.out.println("노트북 충전 중...");
}
}
class HandPhone {}
class SmartPhone extends HandPhone {
public void charge() {
System.out.println("스마트폰 충전 중...");
}
}
문제3
package interface_;
public class Ex5 {
public static void main(String[] args) {
// 4. 모듈간 독립적 프로그래밍으로 인한 개발 기간 단축
DesignerClient desinger = new DesignerClient();
desinger.login();
System.out.println("-------------------");
DeveloperClient developer = new DeveloperClient();
developer.login();
}
}
// 개발자와 디자이너 사이의 규칙을 인터페이스로 정의
interface LoginProcess {
// 공통 기능으로 login() 메서드를 정의하여 파라미터와 리턴타입 지정
public abstract String login(String id, String pass);
}
// 디자이너의 경우
// => 로그인 처리 과정은 중요하지 않고 전달 데이터와 리턴 데이터만 중요함
// => LoginProcess 인터페이스를 구현하는 클래스를 정의하여
// login() 메서드 구현
class Designer implements LoginProcess {
@Override
public String login(String id, String pass) {
// 메서드에 전달되는 데이터가 정확한지만 확인하고
// 리턴값이 외부로 잘 전달되는지만 확인하면 된다!
System.out.println("디자이너가 전달받은 아이디 : " + id);
System.out.println("디자이너가 전달받은 패스워드 : " + pass);
return "성공";
}
}
class DesignerClient { // 디자이너
Designer designer = new Designer();
public void login() {
String id = "admin";
String pass = "1234";
// login() 메서드를 호출하여 파라미터로 아이디, 패스워드를 전달하고
// 로그인 결과로 리턴되는 값을 출력하여 확인 작업 수행
String result = designer.login(id, pass);
System.out.println("로그인 결과 : " + result);
if(result.equals("성공")) {
// 로그인 성공 페이지로 이동 처리 작업 수행
} else {
// 로그인 실패 페이지로 이동 처리 작업 수행
}
}
}
// 개발자의 경우
// => 전달받은 값은 중요하지 않고 전달받은 값을 사용하여 로그인 처리 작업 수행
// 로그인 처리 후 리턴되는 값이 정상적으로 전달되는지만 확인
// => LoginProcess 인터페이스를 구현하는 Develper 클래스 정의
class Developer implements LoginProcess {
// 추상 메서드 구현 필수!
@Override
public String login(String id, String pass) {
// 디자이너로부터 전달받은 아이디와 패스워를 사용하여
// 로그인 작업을 처리한 후 "성공" 또는 "실패" 문자열 리턴
System.out.println("개발자가 전달받은 아이디 : " + id);
System.out.println("개발자가 전달받은 패스워드 : " + pass);
System.out.println("아이디와 패스워드로 로그인 작업 처리 완료!");
return "성공";
}
}
class DeveloperClient {
Developer developer = new Developer();
public void login() {
// 디자이너가 전달하게 될 아이디와 패스워드를 임의로 설정하여 전달하고
// 개발자 코드를 통해 로그인 작업을 수행한 후
// 리턴되어지는 값이 정상적인지만 확인하면 된다
String result = developer.login("admin", "1234");
System.out.println("로그인 결과 : " + result);
}
}
문제4
package interface_;
public class Test5 {
public static void main(String[] args) {
Photoshop p = new Photoshop();
p.draw(new Circle()); // Shape s = new Circle() 과 동일
p.draw(new Rectangle()); // Shape s = new Rectangle() 과 동일
}
}
// ------------------------------------------------------
// 모든 도형의 그리기 기능을 제공하는 Shape 인터페이스 정의
// => draw() 추상메서드 정의(리턴값 없음, 파라미터 없음)
interface Shape {
public abstract void draw();
}
// Shape 인터페이스를 구현하는 Circle, Retangle 클래스 정의
// => 추상메서드 draw() 를 오버라이딩하여 각자 도형 그리기 기능을 구현
// ex) Circle 은 "원 그리기!", Rectangle 은 "사각형 그리기!" 출력
// => Circle : 멤버변수(반지름, radius, 실수형, 접근제한자: private)
// Setter() 메서드, draw() 메서드("원그리기! 반지름 : 반지름길이")
// => Rectangle : 멤버변수(가로, width, 실수형, 접근제한자 : private
// (세로, height, 실수형, 접근제한자 : private)
// => Rectangle : Setter() 메서드, draw() 메서드
// (가로 : 가로길이, 세로 : 세로길이 의 사각형 그리기!)
class Circle implements Shape {
private double radius = 3.0;
public void setRadius(double radius) {
this.radius = radius;
}
@Override
public void draw() {
System.out.println("원 그리기! - 반지름 : " + radius);
}
}
class Rectangle implements Shape {
private double width = 2.0;
private double height = 1.5;
public void setWidth(double width) {
this.width = width;
}
public void setHeight(double height) {
this.height = height;
}
@Override
public void draw() {
System.out.println("가로 : " + width + ", 세로 : " + height + " 의 사각형 그리기!");
}
}
class Photoshop {
// draw() 메서드 정의
// => Circle, Rectangle 인스턴스를 전달받아 해당 인스턴스의 draw() 호출
// 두 인스턴스의 공통 타입으로 Shape 인터페이스를 파라미터 타입으로 지정
public void draw(Shape s) { // Circle&Rectangle -> Shape 업캐스팅
// Shape 타입 변수의 draw() 메서드 호출 시 실제 인스턴스의 draw() 호출됨
s.draw();
}
}
728x90
반응형
'BackEnd > Java' 카테고리의 다른 글
1013 JAVA - String 객체 (문자열) (0) | 2022.10.13 |
---|---|
1013 JAVA - 오브젝트 클래스 Object Class (equals 메서드) (0) | 2022.10.13 |
1006 JAVA - 상수, 인터페이스 (0) | 2022.10.06 |
1005 JAVA - Polymorphism 다형성 (2) (0) | 2022.10.05 |
1005 JAVA - 동적 바인딩 , 정적 바인딩 (0) | 2022.10.05 |