마지막소스 첨부는

자 이제 마지막 리팩토링이네영..

책에선 제네릭을 이용한 다양한 형태로의 변환이 되는 자유로운 팩토링을 보였는데 정말 새롭고 유용한 방법인것 같아서 강추!!

암튼 소스는 다음과 같습니다..

만약 String값을 이용해서 덧붙이는 기능을 사용하고자 할때에는 이런식으로 바꿔주면 되는군요.

우선 Test해볼 메소드입니다.(여기서 파일안에 있는 숫자를 순서대로 덧붙이는 메소드입니다)

@Test public void concatenateOfNumber() throws IOException{
String concateStr = calculator.concatenateString(filepath);
assertThat(concateStr,is("1243"));
}

public class Calculator {
public <T> T lineReadTempleter(String filepath,LineCallback<T> callback,T initVal) throws IOException{
BufferedReader br = null;
try {
   br = new BufferedReader(new FileReader(filepath));
   String line = null;
   T res = initVal;
   
   while( (line = br.readLine()) != null ){
    res = callback.doSomethingWithLine(line, res);
   }
   return res;
} catch (IOException ie) {
// TODO: handle exception
ie.printStackTrace();
} catch (Exception e){
e.printStackTrace();
} finally {
if(br!=null){
try {
br.close();
} catch (Exception e2) {
// TODO: 무시
}
}
}
return null;
}
public Integer calcSum(String filepath) throws IOException{
LineCallback<Integer> callback = 
new LineCallback<Integer>(){
@Override
public Integer doSomethingWithLine(String line, Integer value) {
// TODO Auto-generated method stub
return Integer.parseInt(line) + value;
}
};
return this.lineReadTempleter(filepath, callback, 0);
}

public int calcMultiply(String filepath) throws IOException{
//TODO Auto-generated method stub
LineCallback<Integer> callback = 
new LineCallback<Integer>(){
@Override
public Integer doSomethingWithLine(String line, Integer value) {
// TODO Auto-generated method stub
return Integer.parseInt(line) * value;
}
};
return this.lineReadTempleter(filepath, callback, 1);
}
//주어진 String을 합치는 메소드
public String concatenateString(String filepath) throws IOException{
LineCallback<String> callback = 
new LineCallback<String>(){
@Override
public String doSomethingWithLine(String line, String value) {
// TODO Auto-generated method stub
return value + line;
}
};
return this.lineReadTempleter(filepath, callback, "");
}
}

여기서 주의해서 봐야할껀 템플릿 메소드에 T라고 제네릭코드 가 들어간다는 점..

암튼 참 도움 많이 되는 책인것 같습니다..

토비의 스프링 화이팅!!
3단계에서 보면 calc()랑 multiply()부분에서

while문에서 가져오는 부분이 중복된다는 걸 볼수 있다.

이걸 템플릿/콜백 패턴으로 중복 제거하면(어디까지 갈것인가..두둥..==ㅣ)

public class Calculator {
public Integer lineReadTempleter(String filepath,LineCallback callback,int initVal) throws IOException{
BufferedReader br = null;
try {
   br = new BufferedReader(new FileReader(filepath));
   String line = null;
   Integer res = initVal;
   
   while( (line = br.readLine()) != null ){
    res = callback.doSomethingWithLine(line, res);
   }
   return res;
} catch (IOException ie) {
// TODO: handle exception
ie.printStackTrace();
} catch (Exception e){
e.printStackTrace();
} finally {
if(br!=null){
try {
br.close();
} catch (Exception e2) {
// TODO: 무시
}
}
}
return 0;
}
public Integer calcSum(String filepath) throws IOException{
LineCallback callback = 
new LineCallback(){
@Override
public Integer doSomethingWithLine(String line, Integer value) {
// TODO Auto-generated method stub
return Integer.parseInt(line) + value;
}
};
return this.lineReadTempleter(filepath, callback, 0);
}

public int calcMultiply(String filepath) throws IOException{
//TODO Auto-generated method stub
LineCallback callback = 
new LineCallback(){
@Override
public Integer doSomethingWithLine(String line, Integer value) {
// TODO Auto-generated method stub
return Integer.parseInt(line) * value;
}
};
return this.lineReadTempleter(filepath, callback, 1);
}
}

이때 콜백으로 처리된 인터페이스는 다음과 같다.

public interface LineCallback {
Integer doSomethingWithLine(String line,Integer value);
}

흠냐...대단하단 말밖에 안나온다...^^;

계속해서 리팩토링 되는 것 같다...(소름끼침..==ㅣ);
템플릿/콜백 패턴은 간단한 정의로는 중복되는 코드들을 인터페이스등을 통한 추출로 인하여 코드의 간결함과 효율성을 높이는 데 있다고 한다.

여기 예제에서는 파일입출력시 try/catch/finally등 자원반납에 관해서 모든 메소드가 중복되고 있다.

그래서 그 부분을 빼고자 하는 것이다.

암튼 적용한 다음의 소스는 다음과 같다. 
(참고 : @Before는 junit실행시 서론, @Test는 본론, @after는 결론) 

JUnit는 main메소드가 없어도 테스트를 실행함..^^

CalcSumTest.java

public class CalcSumTest {
Calculator calculator;
String filepath;
@Before public void setUp(){
calculator = new Calculator();
filepath = getClass().getResource("numbers.txt").getPath();
}
@Test public void sumOfNumber() throws IOException{
int sum = calculator.calcSum(filepath);
System.out.println(sum);
assertThat(sum,is(10));
}
}

그리고 템플릿 메소드와 계산메소드가 있는 계산 클래스
public class Calculator {
public Integer calcTemplete(String filepath,BufferedReaderCallback callback) {
BufferedReader br = null;
int result = 0;
try {
   br = new BufferedReader(new FileReader(filepath));
 
   result = callback.doSomethingWithReader(br);
} catch (IOException ie) {
// TODO: handle exception
ie.printStackTrace();
} catch (Exception e){
e.printStackTrace();
} finally {
if(br!=null){
try {
br.close();
} catch (Exception e2) {
// TODO: 무시
}
}
}
return result;
}
public Integer calcSum(String filepath){
BufferedReaderCallback callback = 
new BufferedReaderCallback(){
@Override
public Integer doSomethingWithReader(BufferedReader br){
// TODO Auto-generated method stub
int sum = 0;
try {
String line = "";
while( (line = br.readLine()) != null ){
sum += Integer.parseInt(line);
}
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
return sum;
}
};
return calcTemplete(filepath,callback);
}
}

그리고 콜백 인터페이스

public interface BufferedReaderCallback {
Integer doSomethingWithReader(BufferedReader br);
}

그럼 만약 곱하기를 추가한다고 가정하면 어떻게 하면 될까?

참 간단해진다.

우선 테스트 코드 하나 넣고
@Test public void mutiplyOfNumber() throws IOException{
int multiply = calculator.calcMultiply(filepath);
assertThat(multiply,is(24));
}

그다음 SUM이랑 비슷한 메소드만 카피 앤 패스트 하면 된다.
public int calcMultiply(String filepath) {
// TODO Auto-generated method stub
BufferedReaderCallback callback = 
new BufferedReaderCallback(){
@Override
public Integer doSomethingWithReader(BufferedReader br){
// TODO Auto-generated method stub
int multiply = 1;
try {
String line = "";
while( (line = br.readLine()) != null ){
multiply *= Integer.parseInt(line);
}
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
return multiply;
}
};
return calcTemplete(filepath,callback);
}


package springbook.learningtest.template;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class Calculator {
public Integer calcSum(String filepath) {
BufferedReader br = null;
int sum = 0;
try {
br = new BufferedReader(new FileReader(filepath));
String line = null;
while( (line=br.readLine())!=null ){
sum += Integer.parseInt(line);
}
br.close();
} catch (IOException ie) {
// TODO: handle exception
ie.printStackTrace();
} catch (Exception e){
e.printStackTrace();
} finally {
if(br!=null){
try {
br.close();
} catch (Exception e2) {
// TODO: 무시
}
}
}
return sum;
}
}

출처 : 1400페이지짜리 '토비의 스프링' 서적
간단한 파일 입출력 리펙토링 과정을 보여준다.

우선 기본적으로 알아야 하는 건 JUnit 방법이다..

검색해보면 금방 알수 있으므로 해보삼^^

실제 계산하는 Calculator.java

package springbook.learningtest.template;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class Calculator {
public Integer calcSum(String filepath) throws IOException{
BufferedReader br = new BufferedReader(new FileReader(filepath));
int sum = 0;
String line = null;
while( (line=br.readLine())!=null ){
sum += Integer.parseInt(line);
}
br.close();
return sum;
}
}

이걸 이용하는 구현 클래스는 CalcSumTest.java

여기서 빨간부분을 주의해서 보도록..

package springbook.learningtest.template;

import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;

import java.io.IOException;

import org.junit.Test;

public class CalcSumTest {
@Test
public void sumOfNumber() throws IOException{
Calculator calculator = new Calculator();
int sum = calculator.calcSum(getClass().getResource("numbers.txt").getPath());
assertThat(sum,is(10));
}
}


여기서 문제점은 try/catch/finally를 안넣어서 중간에 예외가 발생하면 자원반납을 안해줘서 문제가 됨.

그래서 2단계에선 묶어줌..

+ Recent posts