interface 2

Mia Lee·2022년 1월 8일
0

JAVA

목록 보기
85/98
package ex_interface;

public class Ex2 {

	public static void main(String[] args) {

		/*
		 * 인터페이스의 필요성
		 * 
		 * - 모듈 교체가 쉬움
		 * 
		 */
		
		// 다형성을 활용하지 않는 방법
		LaserPrinter lp = new LaserPrinter();
		lp.print("Ex.java");
		
		DotPrinter dp = new DotPrinter();
		dp.print("Ex.java");
		
		System.out.println("================================");
		
		// 일반적인 다형성 활용
		// 부모 인터페이스 타입으로 업캐스팅하여 사용
		// => 인터페이스 내에 존재하는 멤버(상수, 추상메서드)에만 접근 가능
		Printer p = new LaserPrinter();
		p.print("Ex.java");
		
		p = new DotPrinter();
		p.print("Ex.java");
		
		System.out.println("===================================");
		
		// 별도의 클래스를 정의하여 부모 인터페이스 타입 객체를 전달받아 사용
		// => 인터페이스의 멤버 외에 별도의 클래스에서 정의한 멤버도 사용 가능
		
		// PrintClient 인스턴스 생성
		PrintClient pc = new PrintClient();
		pc.setPrinter(new LaserPrinter());
		pc.print("Ex.java");
		
		pc.setPrinter(new DotPrinter());
		pc.print("Ex.java");
		
		// 만약, InkjetPrinter가 추가되더라도 Printer를 구현했다면
		// 별도로 PrintClient 클래스를 수정할 필요 없이
		// setPrinter() 메서드 파라미터로 InkjetPrinter 객체만 교체하면
		// 얼마든지 새로운 Printer 타입 객체를 다룰 수 있다!
		
	}

}

interface Printer {
	public void print(String fileName);
	
}

class LaserPrinter implements Printer {

	@Override
	public void print(String fileName) {
		System.out.println("LaserPrinter로 출력중 - " + fileName);
	}
	
}

class DotPrinter implements Printer {

	@Override
	public void print(String fileName) {
		System.out.println("DotPrinter로 출력중 - " + fileName);
	}
	
}

class PrintClient {
	
	// 인터페이스 타입 Printer 인터페이스를 선언
	private Printer printer; // has-a 관계

	// 외부로부터 Printer 타입 인스턴스를 전달받아 초기화하는 Setter 정의
	public void setPrinter(Printer printer) {
		this.printer = printer;
	}
	
	public void print(String fileName) {
		// Printer 타입 객체 내의 print() 메서드를 호출하여
		// 전달받은 fileName에 내용 출력하도록 요청
		printer.print(fileName);
	}
	
}









package ex_interface;

public class Ex3 {

	public static void main(String[] args) {

		/*
		 * 인터페이스의 필요성
		 * 
		 * - 상속 관계가 없는 클래스끼리 관계 부여 가능
		 * - 기존에 다른 클래스를 상속중일 때 다중 상속이 불가능한데 인터페이스를 활용하여
		 *   상속관계가 아닌 객체간에 공통 인터페이스를 제공으로 새로운 상속 관계를 부여가 가능
		 * - 관계가 없는 객체의 경우 공통 타입이 Object 타입으로 변환하여 관리는 할 수 있으나, 
		 *   각 객체의 메서드 호출을 위해서는 다시 다운캐스팅이 필요하지만, 인터페이스를 통해 상속 관계를
		 *   부여하고, 인터페이스에서 공통 메서드를 추상메서드로 제공하는 경우에는 별도의 다운캐스팅 및
		 *   타입 판별 없이 바로 공통 메서드의 호출이 가능!
		 * 
		 */
		
		Ex3 ex = new Ex3();
		ex.badCase();
		ex.goodCase();
		
	} // main() 끝
	
	// 상속관계가 아닌 객체들을 사용하여 다형성을 적용시켜야 할 경우
	public void badCase() {
		
		// HandPhone, DigitalCamera의 공통 타입은 Object 타입 밖에 없음
		// => 이 때, Object 타입으로 업캐스팅 시 charge() 메서드 호출 불가
		Object obj = new HandPhone(); // 업캐스팅
//		obj.charge(); // Object 타입으로 호출 불가능한 메서드
		// 다운캐스팅 통해 다시 HandPhone 타입으로 변경해야 charge() 호출 가능
		HandPhone hp = (HandPhone) obj;
		hp.charge();
		
		obj = new DigitalCamera();
//		obj.charge(); // 오류 발생! Object 타입으로는 호출 불가능한 메서드!
		
		// -----------------------------------------------------------
		// Object[] 타입으로 HandPhone, DigitalCamera 인스턴스 관리
		Object[] objs = {new HandPhone(), new DigitalCamera()};
		// HandPhone hp = new HandPhone();
		// DigitalCamera dc = new DigitalCamera();
		// Object[] objs = {hp, dc};
		
		// for문을 사용하여 배열 objs의 모든 인스턴스에 차례대로 접근하여
		// 각각의 타입에 맞는 다운캐스팅 수행 후 charge() 메서드를 호출
		for (int i = 0; i < objs.length; i++) {
			if (objs[i] instanceof HandPhone) { // HandPhone 타입인지 판별
				// HandPhone 타입으로 다운캐스팅 가능
				HandPhone hp2 = (HandPhone) objs[i];
				hp2.charge();
				
			} else if (objs[i] instanceof DigitalCamera) { // DigitalCamera 타입인지 판별
				// DigitalCamre 타입으로 다운캐스팅
				DigitalCamera dc = (DigitalCamera) objs[i];
				dc.charge();
				
			}
		}
	}
	
	// 상속관계가 아닌 객체들에게 인터페이스를 활용하여
	// 상속관계를 부여한 후 다형성에 활용할 경우
	public void goodCase() {
		// HandPhone2, DigitalCamera2 객체를 담기 위한 타입으로
		// Object 타입 외에도 Chargealbe 타입도 가능
		Chargeable[] objs = {new HandPhone2(), new DigitalCamera2()};
		
		// for문을 사용하여 Chargealbe[] 타입 내의 모든 객체에 접근하여
		// 상속받아 구현한 공통 메서드 charge() 메서드 호출
		// => Chargealbe 인터페이스에 charge() 메서드가 존재하므로
		//    별도의 다운캐스팅 없이도 charge() 메서드에 접근 가능
//		for(Chargeable obj : objs) {
//			obj.charge(); // 공통메서드를 직접 호출 가능(다운캐스팅 불필요)
//		}
		
		for (int i = 0; i < objs.length; i++) {
			objs[i].charge();
		}
		
	}

}

class Phone {}

class Camera {}

class HandPhone extends Phone {
	
	public void charge() {
		System.out.println("HandPhone 충전!");
	}
	
}

class DigitalCamera extends Camera {
	
	public void charge() {
		System.out.println("DigitalCamera 충전!");
	}
	
}

// HandPhone과 DigitalCamera 사이에 특정 관계를 부여해주기 위해
// 공통 인터페이스인 Chargeable 인터페이스를 정의하고 해당 인터페이스 내에
// 추상 메서드로 charge() 메서드를 정의
interface Chargeable {
	public abstract void charge();
}

// HandPhone2 클래스 정의 - Phone 클래스 상속, Chargeable 인터페이스 구현
class HandPhone2 extends Phone implements Chargeable {

	@Override
	public void charge() {
		System.out.println("HandPhone2 충전!");
	}
}

// DigitalCamera2 클래스 정의 - Camera 클래스 상속, Chargeable 인터페이스 구현
class DigitalCamera2 extends Camera implements Chargeable {

	@Override
	public void charge() {
		System.out.println("DigitalCamera2 충전!");
	}
	
}




package ex_interface;

public class Ex4 {

	public static void main(String[] args) {

		/*
		 * 인터페이스의 필요성
		 * 
		 * - 모듈간 독립적 프로그래밍으로 개발 시간 단축
		 * - 여러 모듈간에 공통된 기능을 구현할 메서드를 인터페이스 내의 추상메서드로 제공하여
		 *   모듈간 통일성 부여
		 * - 각 모듈에서 추상메서드 구현을 통해 각자에게 필요한 기능을 따로 작업한 후 차후 
		 *   결합 시 쉽게 결합 가능
		 * - 따라서, 상대방의 작업 진행상황과 관계없이 개발이 가능하므로 개발 비용이 줄어드는 효과를
		 *   가져오게 된다!
		 *   
		 * ex) 숫자 2개를 입력하여 합을 계산 후 결과를 화면에 출력하는 프로그램
		 * => 디자이너(A) - 개발자(B) 협업 수행 가정 했을 때
		 *    A는 입력받은 데이터를 B에 전달하고 결과값을 기다린 후
		 *    B가 리턴하는 결과를 전달받아 화면에 출력해야하며,
		 *    B는 A가 입력받은 데이터를 전달했을 때, 해당 데이터의 합계를 계산한 후
		 *    다시 A에게 리턴해야한다.
		 *    이 때, 서로 상대방의 작업이 완료되지 않으면 다음 작업 수행이 불가능하므로
		 *    상호간에 작업 내용이 같이 진행되어야 한다.
		 *    따라서, 한 쪽에서 작업이 지연되면 다른쪽도 함께 지연되므로 작업에 소요되는
		 *    비용이 증가하게 됨!
		 * => 이를 해결하기 위해 인터페이스 적용 가능
		 *    A 입장 : "숫자 두 개 전달, 하나의 결과값 리턴받아 출력"
		 *    B 입장 : "숫자 두개 전달받아 계산 후, 하나의 숫자 리턴"
		 * 
		 */
		
		CalculatorDesigner cd = new CalculatorDesigner();
		cd.add();
		
	}

}

interface Calculator {
	// 디자이너와 개발자 모두 사용할 공통 메서드를 추상메서드로 정의
	public int add(int a, int b);
}

// 디자이너가 개발자에게 구현될 코드를 미리 간단히 작성하여 테스트 가능
class CalculatorDesignerDev implements Calculator {

	@Override
	public int add(int a, int b) {
		// 디자이너 입장에서는 전달받은 두 수를 
		// 어떻게 계산할 지는 중요하지 않으며, 단지 확인만 수행하면 됨
		System.out.println("전달된 파라미터 확인 a = " + a + ", b = " + b);
		return 0;
	}
	
}

// 디자이너 입장에서의 코드
class CalculatorDesigner {
	public void add() {
		// 외부로부터 두 개의 숫자를 입력받았다고 가정
		int a = 10, b = 20;
		
		// 개발자에게 두 정수를 전달한 뒤, 결과값으로 정수 1개 리턴받아 출력
		// => 현재 개발자 코드가 완성되지 않았더라도
		//    개발자가 사용할 메서드를 미리 구현한 클래스를 대신 사용 가능
//		CalculatorDesignerDev cal = new CalculatorDesignerDev();
//		int result = cal.add(a, b); // 입력받은 두 정수 전달 후 결과 리턴
//		System.out.println(a + " + " + b + " 의 결과 = " + result);
		
		// 차후 개발자의 코드가 완성되면 해당 객체를 통해 add() 메서드 호출
		CalculatorDeveloper cal2 = new CalculatorDeveloper();
		int result2 = cal2.add(a, b); // 입력받은 두 정수 전달 후 결과 리턴
		System.out.println(a + " + " + b + " 의 결과 = " +result2);
		
	}
}

// 개발자 입장에서의 코드
class CalculatorDeveloper implements Calculator {
	// 외부로부터 정수를 입력받는 코드는 중요하지 않고
	// 전달받은 2개의 정수에 대한 덧셈을 수행한 후 리턴이 잘 되는지 확인만 필요!

	@Override
	public int add(int a, int b) {
		System.out.println("전달받은 파라미터 : " + a + ", " + b);
		return a + b;
	}
	
}







연습


package ex_interface;

public class Test1 {

	public static void main(String[] args) {

		LgTv lt = new LgTv();
		System.out.println("넷플릭스 계정명 : " + lt.netflix);
		
		lt.setNetflixAccount("admin");
		
		System.out.println("넷플릭스 계정명 : " + lt.netflix);
		System.out.println("넷플릭스 계정명 : " + lt.netflix.account);
		
		lt.powerOn();
		lt.volumeUp();
		lt.volumeDown();
		lt.changeChannel(10);
		lt.func3D();
		lt.powerOff();
		
		// showNetflix() 메서드 호출
		// LgTv 인스턴스가 갖고 있는 Netflix 인스턴스 내의
		lt.netflix.showNetflix();
		
	}

}

/*
 * Tv와 Speaker 인터페이스의 공통메서드인 volumeUp(), volumeDown() 을 추출하여 부모인터페이스인
 * VolumeControl 인터페이스로 추상화
 * 
 * Tv 인터페이스 정의 - VolumeControl 상속
 * - 메서드 : powerOn / powerOff / changeChannel - 매개변수(channel, 정수형)
 * 
 * Speaker 인터페이스 정의 - VolumeControl 상속
 * - 메서드 : powerOn / powerOff
 *
 * Netflix 클래스 정의
 * - 멤버변수 : account(문자열)
 * - 생성자 : 매개변수로 문자열 전달받아서 멤버변수에 초기화
 * - 메서드 : showNetflix() => "넷플릭스 기능!" 출력
 * 
 * Netflix 기능이 포함된 LgTv 클래스 정의 - Tv / Speaker 기능 초함
 * => Netflix 기능 포함 : has-a 관계
 * - 메서드 : func3D() => "LgTv 3D 기능!" 출력
 * 
 */

interface VolumeControl { // volume관련 메서드 
	void volumeUp(); // 추상메서드
	void volumeDown();
}

interface PowerControl { // power관련 메서드
	public abstract void powerOn();
	public abstract void powerOff();
}

// Tv 인터페이스 정의
interface Tv extends VolumeControl, PowerControl { // 인터페이스 상속이므로 extends 사용
	public abstract void changeChannel(int channel);
	
}

// Speaker 인터페이스 정의
interface Speaker extends VolumeControl, PowerControl {
	
}

class Netflix {
	String account;

	public Netflix(String account) {
		super();
		this.account = account;
	}
	
	public void showNetflix() {
		System.out.println("넷플릭스 기능!");
	}
	
}

class LgTv implements Tv, Speaker {
	// 넷플릭스 기능은 별도의 상속을 거치지 않고 클래스 내에서 
	// has-a 관계로 포함시켜 사용!
//	Netflix netflix = new Netflix("root"); // 인스턴스를 바로 생성
	Netflix netflix;
	
	public void setNetflixAccount(String account) {
		// 넷플릭스에서 사용할 계정을 전달받아
		// 넷플릭스 인스턴스를 생성하면서 생성자에 계정 전달
		netflix = new Netflix(account);
		
	}

	@Override
	public void volumeUp() {
		System.out.println("LgTv volumeUp()");
	}

	@Override
	public void volumeDown() {
		System.out.println("LgTv volumeDown()");
	}

	@Override
	public void powerOn() {
		System.out.println("LgTv powerOn()");
	}

	@Override
	public void powerOff() {
		System.out.println("LgTv powerOff()");
	}

	@Override
	public void changeChannel(int channel) {
		System.out.println("LgTv changeChannel() - " + channel + " 번");
	}
	
	public void func3D() {
		System.out.println("LgTv 3D 기능()");
	}
	
}

class SamsungTv implements Tv, Speaker {

	@Override
	public void volumeUp() {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void volumeDown() {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void powerOn() {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void powerOff() {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void changeChannel(int channel) {
		// TODO Auto-generated method stub
		
	}
	
	public void smartFunc() {
		System.out.println("SamsungTv 스마트 기능!");
		
	}
	
	
}























0개의 댓글