[자바로 배우는 리팩토링 입문] 6장 클래스 추출

2022. 6. 2. 16:43·Major/Java
728x90

 

1. 리팩토링

1) 클래스 추출

- 기존 클래스에서 필드와 메서드를 추출하여 새로운 클래스로 옮기는 것

 

2) 리팩토링 카탈로그

이름 클래스 추출(Extract Class)
상황 클래스를 작성함
문제 한 클래스가 너무 많은 책임을 지고 있음
해법 묶을 수 있는 필드와 메서드를 찾아 새로운 클래스로 추출
결과 o 클래스가 작아짐
o 클래스의 책임이 명확해짐
x 클래스의 개수가 늘어남

 

 

2. 예제 프로그램

클래스명 역할
Book 책 클래스
Author  저자 클래스(리팩토링으로 작성)
Main  Book 사용법 예제 클래스

1) 리팩토링 전

public class Book {
    private String _title;
    private String _isbn;
    private String _price;
    private String _authorName;
    private String _authorMail;
    public Book(String title, String isbn, String price, String authorName, String authorMail) {
        _title = title;
        _isbn = isbn;
        _price = price;
        _authorName = authorName;
        _authorMail = authorMail;
    }
    public String getTitle() { return _title; }
    public String getIsbn() { return _isbn; }
    public String getPrice() { return _price; }
    public String getAuthorName() { return _authorName; }
    public String getAuthorMail() { return _authorMail; }
    public void setAuthorName(String name) { _authorName = name; }
    public void setAuthorMail(String mail) { _authorMail = mail; }
    public String toXml() {
        String author =
            tag("author", tag("name", _authorName) + tag("mail", _authorMail));
        String book =
            tag("book", tag("title", _title) + tag("isbn", _isbn) + tag("price", _price) + author);
        return book;
    }
    private String tag(String element, String content) {
        return "<" + element + ">" + content + "</" + element + ">";
    }
    // ...
}

- 리팩토링 대상 : 책 정보를 담는 클래스에서 저자 정보를 담는 클래스를 빼는 것

public class Main {
    public static void main(String[] args) {
        Book refactoring = new Book(
            "Refactoring: improving the design of existing code",
            "ISBN0201485672",
            "$44.95",
            "Martin Fowler",
            "fowler@acm.org");

        Book math = new Book(
            "프로그래머 수학",
            "ISBN4797329734",
            "20000원",
            "유키 히로시",
            "hyuki@hyuki.com");

        System.out.println("refactoring:");
        System.out.println(refactoring.toXml());

        System.out.println("math:");
        System.out.println(math.toXml());
    }
}

- 책 두 권의 정보를 XML형식으로 표시하였음

 

2) 리팩토링 실행

① 새로운 클래스 작성

⑴ 클래스의 책임을 어떻게 추출할지 결정

⑵ 추출한 책임을 담당할 새로운 클래스 작성

⑶ 기존 클래스에 새로운 클래스로 링크 작성

 

② 필드 이동

⑴ 기존 클래스에서 새로운 클래스로 필요한 필드 이동

⑵ 이동할 때마다 컴파일해서 테스트

 

③ 메서드 이동

⑴ 기존 클래스에서 새로운 클래스로 필요한 메서드 이동

⑵ 이동할 때마다 컴파일해서 테스트

 

④ 추출한 클래스 검토

⑴ 클래스 인터페이스를 줄일 수 있는가

- 불필요한 메서드를 삭제하거나 접근 제한을 private으로 변경하는 것이 좋음

⑵ 새로운 클래스를 외부에 공개해야 하는가

⑶ 공개한다면 외부에서 수정가능하게 할 것인가

 

3) 리팩토링 후

public class Book {
	private String _title;
	private String _isbn;
	private String _price;
	private Author _author;
	public Book(String title, String isbn, String price, String authorName, String authorMail) {
		_title = title;
		_isbn = isbn;
		_price = price;
		_author = new Author(authorName, authorMail);
		}
	public String getTitle() {
		return _title;
	}
	public String getIsbn() {
		return _isbn; 
	}
	public String getPrice() {
		return _price;
	}
	public String getAuthorName() {
		return _author.getName(); 
	}
	public String getAuthorMail() {
		return _author.getMail(); 
	}
	public void setAuthorName(String name) {
		_author.setName(name);
	}
	public void setAuthorMail(String mail) {
		_author.setMail(mail);
	}
	public String toXml() {
		String author =tag("author", tag("name", _author.getName()) + tag("mail", _author.getMail()));
		String book =tag("book", tag("title", _title) + tag("isbn", _isbn) + tag("price", _price) + author);
		return book;
	}
	private String tag(String element, String content) {
		return "<" + element + ">" + content + "</" + element + ">";
	}
	
	class Author {
		private String _name;
		private String _mail;
		public Author(String name, String mail) {
			_name = name;_mail = mail;
		
		}
		public String getName() { 
			return _name; 
		}
		public String getMail() { 
			return _mail; 
		}public void setName(String name) {
			_name = name; 
		}public void setMail(String mail) { 
			_mail = mail; 
		}
	}
}

 

3. 한 걸음 더 나아가기

1) 양방향 링크는 피한다

- 양방향 링크는 초기화나 관리가 복잡하고 필드나 메서드를 정리하기 어려움

- 하지만 어쩔수 없는경우엔 만들어야 함

(위 예제에선 Author 클래스에서 Book 클래스로 향한 링크가 없기 때문에 저자이름이 00인 책을 찾을 때 모든 book 객체들을 찾아봐야 한다. 반대로 book이름이 00인 저자는 링크가 있기때문에 쉽게 찾을 수 있음)

 

2) 기능 추가와 리팩토링

- 많은 기능을 하고 있는 클래스에서 클래스를 추출하면 

  • 기능 추가의 번잡함이 줄어듦
  • 기능 추가가 더욱 적절한 클래스 안에서 이루어짐

3) 불변 인터페이스

- 불변 인터페이스 : 일반 인터페이스를 읽기 전용으로 만드는 것

- 예를 들어 Author 클래스 인스턴스를 읽기 전용으로 만들고 싶으면 아래와 같이 작성함

public interface ImmutableAuthor {
    public String getName();
    public String getMail();
    //세터 메서드가 없다
}
public class Author implements ImmutableAuthor {
    private String _name;
    private String _mail;
    public Author(String name, String mail) {
        _name = name;
        _mail = mail;
    }
    public String getName() { return _name; }
    public String getMail() { return _mail; }
    public void setName(String name) { _name = name; }
    public void setMail(String mail) { _mail = mail; }
    // ...
}

- 저자 정보를 변경하면 곤란한 경우에는 Author가 아닌 ImmutableAuthor 객체로 _author 필드 값을 넘김

public ImmutableAuthor getAuthor() {
	return _author;
}

book.getAuthor().setName("..")  => 컴파일 에러가 난다.

 

4) 역 리팩토링 : 클래스 인라인화

- 지나치게 클래스 추출하는 것도 문제기 때문에 작은 클래스를 큰 클래스로 합치는 클래스 인라인화가 있음

 

4. 정리

- 클래스에서 추출 가능한 책임을 찾은 후 그 책임과 관련 있는 필드와 메서드를 추출해서 새로운 클래스로 만듬

- 이 리팩토링으로 클래스 책임이 명확해짐

 

5. 연습문제

1) 어떤 클래스에 너무 많은 책임이 몰려 있다면 클래스 추출을 검토한다(O)

2) 클래스 추출을 한 클래스끼리는 양방향 링크를 거는 게 좋다(X)

3) 클래스 추출로 만든 새로운 클래스에는 내부를 어떻게 구현했는지 잘 알 수 있는 이름을 붙이는 게 좋다(X)

- 내부 구현이 아닌 해당 클래스의 책임을 알 수 있는 이름이 좋다.

728x90
저작자표시 비영리 (새창열림)

'Major > Java' 카테고리의 다른 글

[자바로 배우는 리팩토링 입문] 8장 분류 코드를 하위 클래스로 치환  (0) 2022.06.07
[자바로 배우는 리팩토링 입문] 7장 분류 코드를 클래스로 치환  (0) 2022.06.04
[자바로 배우는 리팩토링 입문] 5장 메서드 추출  (0) 2022.06.01
[자바로 배우는 리팩토링 입문] 4장 널 객체 도입  (0) 2022.04.15
[자바로 배우는 리팩토링 입문] 3장 어서션 도입  (0) 2022.04.15
'Major/Java' 카테고리의 다른 글
  • [자바로 배우는 리팩토링 입문] 8장 분류 코드를 하위 클래스로 치환
  • [자바로 배우는 리팩토링 입문] 7장 분류 코드를 클래스로 치환
  • [자바로 배우는 리팩토링 입문] 5장 메서드 추출
  • [자바로 배우는 리팩토링 입문] 4장 널 객체 도입
BeNI
BeNI
코딩하는 블로그
  • BeNI
    코딩못하는컴공
    BeNI
  • 전체
    오늘
    어제
    • Menu (253)
      • My profile (1)
      • 회고 | 후기 (8)
      • Frontend (65)
        • Article (11)
        • Study (35)
        • 프로그래머스 FE 데브코스 (19)
      • Backend (0)
      • Algorithm (58)
        • Solution (46)
        • Study (12)
      • Major (111)
        • C&C++ (23)
        • Java (20)
        • Data Structure (14)
        • Computer Network (12)
        • Database (15)
        • Linux (6)
        • Architecture (3)
        • Lisp (15)
        • OS (1)
        • Security (2)
      • etc (2)
  • 링크

    • 깃허브
    • 방명록
  • 인기 글

  • 최근 댓글

  • 최근 글

  • 태그

    데브코스
    C++
    리팩토링
    백준
    react
    lisp
    자료구조
    파일처리
    프로그래머스
    Algorithm
  • hELLO· Designed By정상우.v4.10.2
BeNI
[자바로 배우는 리팩토링 입문] 6장 클래스 추출
상단으로

티스토리툴바