앞선 포스트에서는 Interface를 사용해서 같은 Interface를 구현한 테스터 클래스들을
한번에 실행하여 그 결과를 볼 수 있는 Tester.java 까지 구현하였습니다.


Tester.java
새로운 테스터가 추가되어야 한다면
ProductTester Interface를 구현하여 위 Tester.java 에서 그 클래스를 add해주면 되었습니다.

하지만, 매번 이렇게 테스터가 추가 될 때 마다
클래스를 수정하고, 컴파일해서 배포를 해야 한다면 조금 귀찮은 작업이 될 수도 있습니다.
그리고, 실제로 현장에서 사용되는 테스터 클래스가 한두개가 아닐 수 있는데
그렇게 되면 Tester.java의 클래스는 addTester 메서드로 도배가 될 것 입니다.

그래서 간단하게 프로퍼티를 사용해서 , 테스트 할 테스터 클래스들을 선언하고
이를 이용해서 Tester.java 를 구현해보려고 합니다.

일단 프로퍼티 파일을 만들고 거기다가 테스트 할 테스터 클래스들을 기술합니다.

그리고 이 프로퍼티 파일을 읽어서 해당 테스터 클래스들을 동적으로 생성하고
이를 이용해서 테스트를 수행하는 것이지요..


tester.properties Tester.java
프로퍼티 파일에서 테스터 클래스들을 가져와서 Class.forName 을 통해서 해당 class의 Class instance를 얻고
(관련 글 참조 : http://devyongsik.tistory.com/292) 이를 사용하여 객체를 생성해서 테스트를 수행하는 코드 입니다.
프로퍼티 파일에 대한 stream을 얻는 방법은 위의 두가지 경우처럼 절대경로를 이용하거나, Class의 getResource 메서드를 사용하여 클래스패스에 있는 프로퍼티를 읽어 올 수 있는 방법이 있습니다.
(관련 글 참고 : http://devyongsik.tistory.com/171)

여기서 얘기하고 싶은 것은 저것이 아니기 때문에.. 일단 넘어가고... 아무튼 프로퍼티로 부터 클래스의 full name을 얻어와서
이를 사용해 동적으로 객체를 생성하게 되어 있습니다.
꼭 .properties일 필요는 없고 xml로 만들어서 사용하셔도 되겠죠..

만약에 테스터 클래스가 추가 되어야 한다고 하면 위 상태에서는 소스를 건드릴 것이 아무것도 없습니다

단지, properties 파일에 추가 될 테스터 클래스의 이름을 추가해주기만 하면
알아서 테스트를 실행하게 됩니다.

이정도만 해도 꽤 쓸만 할 것 입니다. (아닐까요..ㅋㅋ)

아직 인터페이스를 사용하는 것이 왜 좋은지 감이 안 오신다면..
위 코드를 인터페이스를 사용하지 않고 한번 구현해보세요.

다음에는 Annotation을 사용해서 만들고자 하는 최종 버전을 작성해 보겠습니다.
Posted by 용식
앞선 포스트에서 테스트 프로그램을 만들어서 돌리는 간단한 프로그램을 만들었습니다.
물론 내부 로직은 다 빠지고 형태만 보여주기 위한 코드였고요..

최종적으로 나온 Tester.java 의 모습은 아래와 같았습니다.



2개의 테스터 클래스가 보입니다. 내부로직으로 구현되어 있지는 않지만
ProductListsComapreTester.java는 상품 리스트를 검색해서 그 정렬 순서가 원하는 순서대로
정렬되어 있는지 확인하는 Tester 클래스입니다.

ProductRankScoreTester.java는 다른 두개의 검색 서버가 있다고 가정 할 때 각각의 서버에 검색을 하고
그 두개의 결과가 일치하는지 확인하는 Tester 클래스입니다. 물론, 내부 로직은 구현을 하지 않았습니다.

일단, 앞에서 이제 인터페이스를 사용한다고 하였는데
Tester.java를 보시면 어떻게 인터페이스를 만들면 좋을지 대충 짐작도 되실겁니다.

바로 만들어서 그 인터페이스를 사용해보겠습니다.


ProductTester.java
그리고, 이를 구현하도록 위 두개의 테스터 클래스를 수정해보겠습니다.


ProductRankScoreTester.java ProductListsCompareTester.java
이미 isPassed라는 이름의 메서드를 공통적으로 가지고 있었기 때문에 쉽게 수정이 가능합니다. 그럼,
Tester.java는 어떻게 바뀔까요?


Tester.java
각 구현체 클래스를 사용 해 참조 변수를 선언하던 부분이 인터페이스를 사용하여 선언하는 것으로 수정되었습니다.
이 상태로 후배에게 "어떤 이점이 있을 것 같아?" 라고 얘기했더니 tester라는 참조 변수를 재사용 할 수 있다. 라고 대답
하더군요...

ProductTester tester = new ProductListsCompareTester();
boolean isPassed = tester.isPassed();
tester = new ProductRankScoreTester();
isPassed = tester2.isPassed();

"재사용하면 뭐가 좋은데?" 라고 하니
"메모리... " 우물쭈물 대답하더군요...

재사용하는 것은 객체여야하지 변수를 재사용하는 것은 이득이 없다라고 얘기 해주고.. 또, 객체도 어지간히 크거나 무거운 객체가 아니며 재사용 하는 것이 아주 눈에 띄는 성능 향상을 가져오지 않는다고 얘기해주었습니다.

개인적으로는, 변수의 재사용을 잘 하지 않는 편입니다.

아무튼.. 변수의 재사용이 중요한 것이 아니라 참조 변수를 선언하는 데 사용한 타입 (ProductTester 인터페이스)이 같다는 것이
중요한 것 입니다.

테스트를 하기 위한 로직은 ProductTester 인터페이스를 구현한 각각의 구현 클래스들이 알아서 가지고 있고,
외부에서는 isPassed라는 메서드를 통해 그 테스트 결과가 통과인지 실패인지를 알 수만 있으면 됩니다.
그것이 이번에 간단하게 만들어보려고 하는 테스트 프레임워크 (라고 이름 붙이기도 민망한..)의 핵심입니다.

그럼, 인터페이스를 사용하였으니 이를 사용해서 Tester 클래스들을 모두 가져와서
일괄적으로 테스트를 실행 할 수 있는 MultiTester를 만들어보겠습니다.


MultiTester.java
인터페이스를 사용했기 때문에 ProductTester라는 Type을 갖는 List를 사용하여 모든 Tester 클래스를 받고
이를 사용해서 테스트를 일괄적으로 수행하고 있습니다.
addTester메서드의 파라메터가 ProductTester인것과 isAllPassed 메서드의 for문에 마찬가지로 ProductTester 인터
페이스가 사용 된 것을 잘 보시고요...

그럼 이것을 어떻게 사용 할까요?


Tester.java
테스터 클래스들이 같은 인터페이스를 구현하고 있기 때문에
MultiTester는 위 처럼 다른 구현 클래스를 하나의 ProductTester로써 가질 수 있게 됩니다.

테스터 클래스는 현재 사용하고 있는 Service나 Controller 혹은 DAO를 사용하여
테스트를 수행하도록 되어 있다고 가정해보면..
위 Tester를 하루에 한번씩 돌리도록 스케쥴을 지정하고 그 결과로 메일로 발송하게 되면
누군가 Service나 DAO의 로직을 잘못 수정했을 때 위 테스터에 의해서 그 결과를 바로 받을 수 있을 것 입니다.

이게 아주 초보적인 테스트 자동화라고 볼 수도 있겠죠..

만약에 서비스 클래스가 늘어나서 그 서비스 클래스를 테스트 하기 위한 테스터를 만들어
위 자동 테스터 프레임워크에 적용하고 싶다면?

간단합니다..
ProductTester 인터페이스를 구현하고, 이를 MultiTester에 add만 해주면 되는 것이죠..



NewServiceTester.java Tester.java
Tester에 저렇게 추가만 하면 되지요...

그런데, 테스터가 추가 될 때마다 Tester 클래스를 수정해줘야 하는 것은 조금 귀찮을 수 있습니다.
다음에는 이것을 프로퍼티를 사용해서 클래스 수정 없이 테스터를 추가해주는 것을 보겠습니다.

reflect에 대한 이야기가 나오겠고요...

그 이후에는 Annotation을 사용해서 아예 프로퍼티 조차 수정하지 않아도
테스터가 적용 되도록 해보겠습니다....


Posted by 용식
Java책을 보면서 공부를 하면
Interface에 대한 내용을 볼 수 있습니다.

제 기억으로 처음 자바를 공부하는 입장에서 가장 기억에 남았던 것은
인터페이스는 내용 없는 메서드 정의가 가능하고, 이것을 구현하는 클래스들은
만드시 그 메서드의 내용을 구현해야 한다. (추상 클래스는 여기서 논외로 하겠습니다.)
라는 것이었습니다.

그리고 나오는 예제들이 TV나 그런거였고..
여기서 다형성과 캐스팅에 대한 얘기들도 같이 나옵니다.

그렇게해서 Interface를 공부했는데..
그렇게 공부를 하고 몇 년이 지나도록 업무에 있으면서
도무지 이 Interface가 왜 좋은지 이해를 못 하고 있었습니다.

Interface로 메서드를 정의하고
그것을 구현하는 클래스들이 항상 같은  메서드를 갖도록 한다.

이것이 사람/동물 이런 관계로 OOP를 설명하는 책의 내용과 맞물려서
관련이 있는 is-a 관계가 성립하는 클래스들끼리 인터페이스를 정의해서 사용하는구나..
싶었는데.. Cloneable이나 Comparable 같은 것을 보면 또 그렇지도 않습니다.

최근에 부사수(2년차)에게 검색에서 리스트 2개를 가져와서
비교하는 테스트 프로그램을 만들라고 하였습니다. Junit 같은 것을 사용해도 되는데
좀 더 기본적인 것들을 가르쳐주고 싶어서 일단 마음대로 구현해보라고 하였습니다.

그러는 와중에 나온 것들을 정리하면 좋겠다 싶어서 여기다가 정리해봅니다.

후배가 처음 가져온 소스는 main 메서드를 사용해서
작성한 프로그램이었습니다.



Tester.java

기능은 잘 작동하는데 좀 더 가르쳐주기 위해서
테스트를 수행하는 메서드를 하나의 클래스로 다시 만들라고 하였습니다.
그리고 결과를 print하지말고 boolean으로 return하여 테스트를 통과했는지 못 했는지를 나타낼 수 있도록
네이밍을 다시 하라고 하였습니다.

그래서 아래와 같은 클래스가 만들어졌습니다.

ProductListsCompareTester.java

실행은 아래처럼 될 것 입니다.
Tester.java
이 상태에서 이번에는 검색을 한 다음 그 정렬순서가
점수에 맞게 되었는지 확인하는 테스트케이스를 더 만들어보라고 하였습니다.
ProductRankScoreTester.java

사용은 아래와 같이 될 것 입니다.
Tester.java

이렇게 해서 두개의 테스터 클래스가 생겼습니다.
ProductListsCompareTester와  ProductRankScoreTester 이렇게 두개의 클래스가 생긴거죠..

물론 같은 이름의 메서드를 사용하도록 제가 유도를 하기는 하였지만..
이렇게 하고 나서 후배에게 "공통되는 메서드를 인터페이스로 빼서 인터페이스를 만들고
저 Tester 클래스들을 인터페이스를 상속하게 하여 구현하면 어떨까? 라고 물어보고 그렇게 작업을 해보라고 하였습니다.

이제 한번 인터페이스를 활용해보려고 합니다.
Posted by 용식

TV들이 아주 작 작동하는 군요. 하지만 사용자는 인터페이스를 통해서 조작하고 있습니다. 물론 TV를 만들어지만 TV내부는 각 회사에서 알아서 하겠죠. 하지만 TV의 인터페이스가 같기 때문에 어느 회사의 제품이나 조작하기는 편할 것입니다. 데이터베이스의 드라이브도 이러한 방식으로 Sun사에서 Database의 interface를 정의해두고 그 interface에 의해서 만들도록 하는 것입니다.

 

                           TVBoard s = new SSgTV();

                           TVBoard g = new LGgTV();

 

이 두 줄을 유심히 봐 주시기 바랍니다. 일반적인 Upcasting의 개념이 그대로 interface에서도 적용되고 있습니다. 이것은 인터페이스 자체도 클래스이기 때문에 Upcasting을 적용할 수 있는 것입니다. 사용자 측면에서 인터페이스가 고정되어 있으면 많은 시간과 비용을 절약할 수 있을 것입니다. 모든 TV가 같은 인터페이스로 되어 있다면 하나의 조작법만 안다면 모든 TV를 조작 할 수 있을 것입니다. 이것이 바로 인터페이스의 본질적인 역할입니다. 말 그대로 인터페이스는 인터페이스입니다.

 

 

 

 

 

 

5.4.3 Abstract와 Interface에서의 Upcasting

 

 우리는 upcasting을 일반적인 클래스 구조에서만 살펴 보았습니다. 하지만 이것은 abstract와 interface에서도 적용 되어 질 수 있습니다. 그 구조는 다음과 같을 것입니다.

그런데 다음과 같은 질문을 할 수 있을 것입니다.

n         abstract는 객체를 생성할 수 없다.

n         interface는 객체를 생성할 수 없다.

이러한 말에 부딪히게 됩니다. abstract와 interface는 객체를 생성하는 것이 아니라 만들어진 객체를 할당 받는 것입니다. 형은 같기 때문에 이러한 것이 가능합니다. abstract나 interface로는 new연산자를 사용하지 않습니다. 단지 만들어진 객체를 upcasting을 이용해서 넘겨 받을 뿐입니다. 이러한 것은 많은 장점을 내포하고 있습니다. 알고 보면 interface는 빈 껍데기에 불과 합니다.

사용자는 용량이 적은 빈 껍데기만 가지고 있어도 모든 작업을 전부 수행 할 수 있습니다. 우의 그림에서는 설명을 위해서 임의의 interface만들었습니다. 이 인터페이스를 이용해서 우리는 다음과 같은 코드를 작성 할 수 있을 것입니다.

 

n         Shape s = new Circle();

n         Action a = new Circle();

n         Attribute ab = new Circle();

n         Circle c = new Circle();

 

이 코드의 장점은 사용자는 내부를 몰라도 된다는 것입니다. interface만 있다면 어떠한 작업이라도 전부 할 수 있습니다. 이러한 장점은 우리가 직접 만들어서 작업하기 보다는 내부적으로 이루어지는 경우가 아주 많습니다. 하지만 이것을 잘 이용하면 보다 정교한 프로그램작업을 할 수 있으리라 생각됩니다.

 

 

5.4.4 결론

여러분들이 잘 모르고 사용하는 Upcasting은 아주 많은 장점을 제공하고 있습니다. 자바도 여러 종류이지만 고급 자바로 갈수록 이 Upcasting개념은 더욱 철저하게 적용됩니다. RMI, Enterprise Java Beans, Mobile Java, Personal Java, Java Dynamic Management Kit등 사용 안 되는 곳이 없습니다. 어차피 자바를 배우면 실무에서 다루지 않겠습니까?

 

다형성을 주장하는 Upcasting의 장점은 abstract, interface, encapsulation, overriding 등 아주 많은 부분과 관련이 있습니다. 이 upcasting과 interface에 대해서 제대로 이해했다면 자바는 바로 여러분들의 손에 있는 것입니다.

 

출처 : www.jabook.org

Posted by 용식

[Java] Interface기초

Java 2008.05.26 10:28
 

자바에서 인터페이스의 사용


interface move 라는 것이 있고 이넘은


up, down, left, right 라는 메서드를 가지고 있다고 하면


이녀석을 구현하는 클래스들은 move의 대상 (예를 들면 게임에서 마린, 탱크 등등)에 따라서


실제적으로 구현해야 하는 방식이 달라질 것이다.


이것을 interface를 사용하지 않으면 무언가 바뀌거나 할때마다 해당 클래스를 생성하고 있는 소스를 찾아서 하나하나 바꿔줘야 한다.


하지만 interface를 사용해서


class marineMove implements Move {

   up()....

..

}


class wraithMove implements Move {

  up() ..

}


해당 클래스들을 구현한 다음 다른 소스에서


Move move = getMoveService(); <- (getMoveService는 marineMove나 wraithMove중

해당되는 것을 생성하여 리턴하여 준다.)


이렇게 사용을 하면 (업캐스팅 원리)


즉, move.up().. move.down()..


이렇게 사용하면 실제 생성한 클래스의 메서드가 호출 가능하다.


실제 사용하고 있는 소스의 수정 없이도


사용이 가능하다는 말씀.

Posted by 용식
TAG interface