Whiteship's Note


파트 3: Refactoring Toward Deeper Insight

JEDI/DDD : 2008.10.24 13:05


리팩터링 수준

다른 책들은 너무 로우 레벨로 얘기하는데 그 보다는 도메인에 대한 통찰을 통한 또는 모델이 말하고자 하는 바를 분명하게 하는 것이 시스템의 실용성에 더 지대한 영향을 주는 리팩터링 이라는 이야기..
The refactorings that have the greatest impact on the viability of the system are those motivated by new insights into the domain or those that clarify the model's expression through the code.
Deep Model

모델을 좀 더 심도있게 바라보도록.. 피상적인 모습으로만 축출한 모델이 다가 아니라는거...
A deep model provides a lucid expression of the primary concerns of the domain experts and their most relevant knowledge while it sloughs off the superficial aspects of the domain.
Supple Design

변경을 반영할 수 있는 설계가 되어야 한다.

In a process of constant refactoring, the design itself needs to support change.
Deep Model과 Supple Design은 Model-Driven Design을 뒷받침하는 두 개의 축이다.
A MODEL-DRIVEN DESIGN stands on two legs. A deep model makes possible an expressive design. At the same time, a design can actually feed insight into the model discovery process when it has the flexibility to let a developer experiment and the clarity to show a developer what is happening.
The Discovery Process

좋은 모델을 찾아가는 과정은 개인의 창의성에 달려있기도 하지만, 여러 패턴을 따를 수도 있겠다.
You will usually depend on creativity and trial and error to find good ways to model the concepts you discover, but sometimes someone has laid down a pattern you can follow.

가끔은 이론적인 얘기로 머리를 환기시키는 것도 좋네요.

'JEDI > DDD' 카테고리의 다른 글

[DDD] User-Familly 구현  (0) 2009.06.22
[DDD] Whiteship's DDD 아키텍처 수정  (6) 2009.06.16
[DDD] Whiteship's DDD 아키텍처  (10) 2009.06.12
[DDD] DDD 입문에 좋은 글  (4) 2009.06.11
파트 3: Refactoring Toward Deeper Insight  (0) 2008.10.24
Factories  (0) 2007.12.25
Aggregates  (0) 2007.12.18
Modules  (2) 2007.12.14
Services  (2) 2007.12.13
DAO vs Repository  (4) 2007.12.04
DTO(Data Transfer Object)  (2) 2007.11.21
top

Write a comment.


서비스 계층 다이어트 시키기

모하니?/Coding : 2008.07.15 14:51


    private void makeNewMonthlySalesSum(Supp supp, Date date) {
        MonthlySalesSum monthlySalesSum = new MonthlySalesSum();
        monthlySalesSum.setDate(date);
        monthlySalesSum.setSupp(supp);
        this.dao.add(monthlySalesSum);

        MonthlySalesSumDetail detail = null;
        for (Branch branch : branchDao.getBranchBySupp(supp.getId())) {
            DTO dto = this.dao.makeMonthlySalesSumDTO(branch, date);
            detail = makeNewMonthlySalesSumDetail(dto, branch, date);
           monthlySalesSum.addSalesCount(detail.getSalesCount());
            monthlySalesSum.addSellingQty(detail.getSellingQty());
            monthlySalesSum.addProfit(detail.getTotalProfit());
            monthlySalesSum.addSuppUnitPrice(detail.getTotalSuppUnitPrice());
            monthlySalesSum.addSuppUnitPriceWithoutText(detail.getTotalSuppUnitPriceWithoutTex());
            if(detail.getSalesCount() > 0){
                detail.setMonthlySalesSum(monthlySalesSum);
                monthlySalesSumDetailDao.add(detail);
            }
        }
        
    }

    private MonthlySalesSumDetail makeNewMonthlySalesSumDetail(DTO dto, Branch branch, Date date) {
        MonthlySalesSumDetail detail = new MonthlySalesSumDetail();
        detail.setBranch(branch);
        detail.setDate(date);
        if (dto != null) {
            detail.setSalesCount(dto.getSalesCount());
            detail.setSellingQty(dto.getSellingQty());
            detail.setTotalProfit(dto.getTotalProfit());
            detail.setTotalSuppUnitPrice(dto.getTotalSuppUnitPrice());
            detail.setTotalSuppUnitPriceWithoutTex(dto.getTotalSuppUnitPriceWithoutTex());
        }
        return detail;
    }


서비스 클래스에 있는 배치 작업 용 메소드들 입니다. 종합 정보를 생성하는데 상세 정보와 함께 묶어서 작업을 하고 있는데 코드가... 참... 거시기 합니다. 클래스에 들어있는 코드의 절반을 이 두 개의 메소드가 잡아먹고 있었습니다.

자세히 보니까 어떤 객체에서 값들을 꺼내서 다른 객체에 전달하는 일들이 전부 입니다. 흠... 옮길 수 있겠다!! 라는 생각이 젤 먼저 들었습니다. 잠시 뒤.. 오.. 당연히 옮겨야 되겠는데? 사실 저렇게 세팅해주는 일이 원래 저 서비스 클래스가 할 일은 아니니까... 걍 자기가 알아서 값들 세팅하면 되지 왜 서비스 계층이 저렇게 일일히 세팅하게 했을까나;; ㅠ.ㅜ (제가 코딩한 겁니다.ㅋㅋ)

그래서 코드를 고쳤습니다.


    private void makeNewMonthlySalesSum(Supp supp, Date date) {
        MonthlySalesSum monthlySalesSum = new MonthlySalesSum();
        monthlySalesSum.setDate(date);
        monthlySalesSum.setSupp(supp);
        this.dao.add(monthlySalesSum);
       
        MonthlySalesSumDetail detail = null;
        for (Branch branch : branchDao.getBranchBySupp(supp.getId())) {
            DTO dto = this.dao.makeMonthlySalesSumDTO(branch, date);
            detail = makeNewMonthlySalesSumDetail(dto, branch, date);
            monthlySalesSum.applyDetail(detail);
            if(detail.getSalesCount() > 0){
                detail.setMonthlySalesSum(monthlySalesSum);
                monthlySalesSumDetailDao.add(detail);
            }
        }
    }

    private MonthlySalesSumDetail makeNewMonthlySalesSumDetail(DTO dto, Branch branch, Date date) {
        MonthlySalesSumDetail detail = new MonthlySalesSumDetail();
        detail.setBranch(branch);
        detail.setDate(date);
        if (dto != null)
            detail.setByDTO(dto);
        return detail;
    }

코드를 1/3 가량 줄일 수 있었습니다. 야호~

서비스 계층은 가볍게 도메인 계층은 두툼하게...
자기가 할 일은 자기가 하자.

top

Write a comment.


엑셀 시트 복사하기(with POI) + 리팩터링

모하니?/Coding : 2008.07.01 13:09


    private void copySheet(HSSFSheet from, HSSFSheet to) {
        HSSFRow firstRow = from.getRow(0);
        HSSFRow secondRow = from.getRow(1);
        HSSFRow thirdRow = from.getRow(2);
        
        HSSFRow firstRow2 = to.createRow(0);
        HSSFRow secondRow2 = to.createRow(1);
        HSSFRow thirdRow2 = to.createRow(2);
        
        Iterator<HSSFCell> iterator = firstRow.cellIterator();
        short col = 0;
        while(iterator.hasNext())
            addCell(firstRow2, col++, iterator.next().getStringCellValue());
        
        col = 0;
        iterator = secondRow.cellIterator();
        while(iterator.hasNext())
            addCell(secondRow2, col++, iterator.next().getStringCellValue());

        col = 0;
        iterator = thirdRow.cellIterator();
        while(iterator.hasNext())
            addCell(thirdRow2, col++, iterator.next().getStringCellValue());
    }

무려 세 번이나 중복되고 있지만, 일단 기능이 제대로 돌아가는지부터 보려고 허겁 지겁 코딩을 하고 결과를 확인해 보니 괜춘하네요. 여기서 그만 둘까도 생각했지만, 에이~ 뭐 시간도 많은데 저런걸 그냥 두긴 뭐하다 싶어서 리팩터링..

    private void copySheet(HSSFSheet from, HSSFSheet to, int fromRowCnt, int toRowCnt) {
        HSSFRow fromRow = null;
        HSSFRow toRow = null;
        for(int i = fromRowCnt ; i <= toRowCnt ; i++){
            fromRow = from.getRow(i);
            toRow = to.createRow(i);
            Iterator<HSSFCell> iterator = fromRow.cellIterator();
            short col = 0;
            while(iterator.hasNext())
                addCell(toRow, col++, iterator.next().getStringCellValue());
        }
    }

리팩터링 하는 김에 좀 더 Generic하게 만들어서 몇 번째 줄부터 몇 번째 줄까지 복사할지 인자로 넘겨주도록 수정 함. 이제 저 메소드는 ExcelUtils로 옮기면 ExelView쪽 코드는 약간 더 깔끔해지겠죠. 그건 뭐 간단하니 생략합니다.
top

Write a comment.


선언된 순서를 변경하는 것은 리팩터링인가?



원문 : IsDeclarationOrderingRefactoring     refactoring     1 September 2004

RefactoringBoundary.

자바 프로그램 내부에 있는 메소드와 필드가 선언된 순서를 변경하는 것은 리팩터링인가요?

근래의 프로그래밍 언어들은 내부에 선언되어 있는 요소들의 순서가 프로그램에 전혀 영향을 주지 않는다. 만약 텍스트 파일 내부에 있는 두 개의 메소드 위치를 서로 바꾸더라도, 프로그램은 아무런 영향을 받지 않는다. 이것을 리팩터링이라고 할 수 있는 이유는 변경으로 인해서 프로그램이 동작하는 방법에 영향을 주지 않기 때문이다. 이것은 설계를 변경하지 않으며, 만약 설계가 바뀐다면 리팩터링이라고 할 수 없다.

리팩터링 정의에서, “이해하기 쉽고 수정하는 비용을 최소화 하도록” 이라는 구문을 사용했다. 선언된 위치를 변경하는 것이 이에 해당하는가? 몇몇 경우에 그럴 수 있다고 생각한다. 클래스 내부에서 서로 관련되어 있는 속성들을 묶어두는 것이 좋다. 그러한 방법으로 속성들을 정렬하면 클래스가 어떻게 동작하는지 이해하는데 도움이 된다. 특히 테스트 케이스를 작성할 때 유용하다. (비록 특정 xunit 구현체에서는, 순서가 실행 결과에 영향을 줄 수 있지만 말이다.) 결국 코드의 이해도를 높여주기 때문에, 선언된 순서 변경을 리팩터링이라고 볼 수 있다.

이것이 내부 구현을 변경하지 않는다는 사실은 중요하지 않다. 메소드의 이름 변경은 실행하는 내용을 변경하지는 않는다. 하지만 이름 변경은 프로그램의 이해도를 높이는 매우 중요한 리팩터링이다.

top

Write a comment.


리팩터링 정의



원문 : DefinitionOfRefactoring     refactoring     

나(Martin Fowler)의 리팩터링 책에서, 리팩터링에 대한 몇몇 정의를 내렸다.

리팩터링 (명사): 소프트웨어의 내부 구조를 보다 이해하기 쉽고 수정하는 비용을 최소화 하도록 하는 변경. 이 때 “주목할 만한” 행동의 변경이 있으면 안 된다.

리팩터링 (동사): “주목할 만한” 행동의 변경 없이, 몇몇 리팩터링들을 적용하여 소프트웨어의 내부 구조를 변경하다.

top

Write a comment.


발견하지 못한 버그를 수정하는 것은 리팩터링인가?



원문 : IsFixingAnUnknownBugRefactoring     refactoring     3 September 2004

RefactoringBoundary.

Przemyslaw Pokrywka가 매우 난해한 질문을 했다. 에서 소개한 리팩터링 중에 하나로 Introduce Null Object라는 것이 있는데, (이것은 매우 유용한 리팩터링으로 Josh의 새 책에서도 다루고 있다) Przemyslaw의 요지는 이 리팩터링이 행동을 바꿀 수 있다는 것이었다. 만약 여러분이 null을 반환하는 메소드를 가지고 있고, 그 반환 값인 null에게 메소드 호출을 하면 null pointer exception을 받게 될 것이다. 이럴 때 Null Object를 사용해서 (예외를 발생시키는 것이 아니라) 기본 행동을 수행하도록 정의할 수 있다.

요즘 다수의 리팩터링들이 행동을 바꾸고 있는데, 이들은 근본적으로 그렇게 하도록 의도된 것 들이다. 예를 들어 Form Template Method를 적용하면, 프로그램이 다르게 동작한다. 핵심적으로 질문해야 할 것은 리팩터링 정의에서 언급한 “주목할 만한” 행동에 해당하는가 이다. “주목할 만한” 행동이란 프로그램이 원래 의도했던 것을 변경했는지 여부를 뜻한다. Introduce Null Object를 사용하면 프로그램에서 반환된 레퍼런스 변수를 조작하는 부분 (특히 null인지 확인하는 부분)을 살펴보아야 한다. 그렇기 때문에 이 리팩터링이 다소 복잡한 것이다.

질문 중에서 가장 흥미로운 부분은 만약에 버그가 있는 부분을 놓치면 어떤 일이 발생하는가 이다. 프로그램 내부의 어디에선가 null 레퍼런스에게 메소드를 호출하는 부분이 있다고 하자. 이 부분을 간과하여 놓쳤고 최종 사용자에게까지 전달되었다면, 리팩터링 전에는 예외를 발생시켰을 것이다. 리팩터링 후에는 기본 행동을 가지게 되고 이것은 사실상 버그를 고친 것에 해당한다. 미처 발견하지 못했던 버그를 고치는 것은 리팩터링일까?

그렇다. 왜냐면 스스로가 지각하지 못했거나 충분히 살펴보지 못한 (버그를 발생시키는) 행동은 “눈에 띄는” 행동이라고 할 수 없기 때문이다. 비록 버그를 알고 있었다 하더라도, 행동을 변함없이 유지하려고 인식했던 버그가 아니라면, 여전히 그것을 리팩터링이라고 불러도 좋다.

이것은 흥미로운 경우이기 때문에, 내 생각을 쉽게 바꿀 수도 있고 이와 비슷한 경계 사례들을 더 조사해 봐야겠다.

여기서 생각해 볼 것 한 가지는 수작업으로 하는 리팩터링과 툴 기반의 리택퍼링의 차이점이다. 손수 하는 리팩터링은 이런 식의 스스로 판단을 할 수 있는 반면에, 툴을 사용할 때는 좀 더 주의해야 한다. 아직은 툴들이 행동을 유지하는 것을 항상 보장하지는 못한다. 심지어, 파일로부터 읽어 들인 이름으로 리플렉션을 사용하여 메소드를 호출하는 경우 rename method는 리팩터링 도중 깨질 수 도 있다.

top

Write a comment.


성능 최적화가 리팩터링인가?



RefactoringBoundary.

프로그램의 성능을 향상 시키기 위해서 변경을 가했을 때, 이것을 리팩터링으로 볼 수 있나요?

성능 최적화와 리팩터링은 둘 모두 프로그램에 같은 변경을 가하기도 하며, 특정한 변경은 둘 모두에 해당하기도 하지만, 이 둘을 별개의 것으로 구분한다.

이 둘을 구분하는 이유는 서로 다른 목적을 가지고 있기 때문이다. 리팩터링을 하는 목적은 코드를 좀 더 쉽게 이해하기 위한 것이며, 성능 최적화의 목적은 좀 더 빠르게 동작하도록 하는 것이다. (예를 들어) 변수 하나를 추가하는 것이 둘 모두를 위한 것이 될 수도 있겠지만, 그 작업을 어떻게 수행하느냐에 따라 본질적으로 둘 중 하나에 해당하게 된다. 리팩터링을 할 때는 코드를 깔끔하게 정리하는 것에 집중해야 한다. 이때, 변경을 성공적으로 수행했는지에 대한 판단은 프로그램을 더 쉽게 이해하도록 변경했는가에 대한 자신의 (주관적인) 평가에 기반하게 된다. 성능 최적화를 할 때는 성능에 집중해야 한다. 프로파일러를 이용하여, 변경을 가하기 전과 후의 성능을 비교함으로써 변경 작업이 진짜로 성능 향상에 도움이 되었는지 확인해야 한다. 만약 성능이 매우 중요한 상황이라면, 차후에 (컴파일러, VM 같은)환경이 변경 됐을 때 그것의 효율성을 다시 테스트 할 수 있도록 변경 사항을 로그로 남겨두어야 한다.

결론적으로 리팩터링과 성능 최적화는 서로 비슷하기도 하고, 다수의 변경을 공유하기도 하지만, 그들의 목적이 다르기 때문에 별개의 것으로 구분한다.

원문 : IsOptimizationRefactoring

top

Write a comment.