리펙토링 루비
4번째 발표
컬렉션 캡슐화
248p
준비
• 컬렉션이란?
– 1개 이상의 엘리먼트들을 담는 자료 구조
– C++ : 컨테이너
• Vector, map, set, hash_map, …
• 컬렉션 캡슐화란?
– 컬렉션을 캡슐화 하여 직접 읽거나 쓰지 않고
컬렉션 접근 메소드를 통해 읽거나 쓰도록 하
는 것.
동기
• 의도되지 않은 자료 조작을 방지
– Add/Remove할 때 추가적인 처리가 필요한
경우
– 상황에 따라 Add/Remove등의 조작을 제한하
고 싶을 경우
– …
• 내부 데이터 구조 노출
– 구조 변경이 어려워짐
– 클라이언트에게 불필요한 인식을 요구
1. add/remove메소드 추가
• def add_course(course)
@courses << course
end
def remove_course(course)
@courses.delete(course)
end
2. 컬렉션 초기화
• Def initialize
@courses = []
end
3. Setter 호출 부분 수정(before)
• kent = Person.new
• courses = []
• courses << Course.new("Smalltalk
Programming", false)
• courses << Course.new("Appreciating
Single Malts", true)
• kent.course =courses
3. Setter 호출 부분 수정(after)
• kent = Person.new
• Kent.add_course(Course.new("Smalltalk
Programming", false))
• Kent.add_course(Course.new("Appreciatin
g Single Malts", true))
4. Getter가 사본을 리턴하도록 수
정
• Def courses
@courses.dup
end
• Def courses
@courses.class
end
5. 컬렉션을 사용하는 객체로 들어
가야 할 기능 추출 및 이동(before)
• number_of_advanced_courses =
kent.courses.select do |course|
course.advanced?
end.size
5. 컬렉션을 사용하는 객체로 들어
가야 할 기능 추출 및 이동(after1)
• def number_of_advanced_courses
kent.courses.select { |course|
course.advanced? }.size
end
5. 컬렉션을 사용하는 객체로 들어
가야 할 기능 추출 및 이동(after2)
• def number_of_advanced_courses
@courses.select { |course|
course.advanced? }.size
end
• Def number_of_courses
@courses.size
end
레코드를 데이터 클래스로 전환
254p
준비
• 레코드란?
– 일련의 기록
• 덤 데이터 객체(dumb data object)란?
– 데이터만 존재하는 클래스(구조체)
– 데이터와 인터페이스가 함께 존재해야 한다는
객체 지향의 원칙과는 맞지 않음
동기
• 레코드 구조를 “가독성 있고 편리하게” 인터페이
싱 할 수 있도록 해줌
– 배열을 객체로 전환과 비슷
• Struct UserData
• {
– Name
– Age
– Email
• }
• Open회원가입Dialog(UserData data)
방법
• 레코드를 나타낼 클래스를 작성
• 클래스에 필드 추가 및 필요한 항목에 대
한 접근 메서드 작성
• 끝
– 나머진 후에 나옴.( 다른 분께서… )
타입 코드를 재정의로 전환
255p
준비
• 타입 코드란?
– 흔히 말하는 enum
• BikeType_Rigid
BikeType_FrontSuspension
BikeType_FullSuspension
• 재정의란?
– 타입별 기능에 대한 재정의를 말함(원서 225p)
동기
• 조건문 제거
– 코드의 복잡성을 낮춤
1. 타입에 해당하는 클래스 작성 및
기본 클래스를 모듈로 변환
• Class RigidMountainBike
include MoutainBike
end
Class FrontSuspensionMountainBike
include MountainBike
end
Class FullSuspensionMountainBike
include MountainBike
end
Class module MountainBike
….
2. 원본 클래스 생성을 원하는 타입
의 클래스 생성으로 대체 및 테스트
• Bike = MountainBike.new(…)
• -> Bike =
FrontSuspensionMountainBike.new(…)
3. 타입 코드에 의존적인 메서드 중 하
나를 각 타입 클래스에 맞게 재정의 및
테스트
• Class RigidMountainBike …
def price
end
Class FrontSuspensionMountainBike …
def price
end
Class FullSuspensionMountainBike …
def price
end
module MountainBike …
def price
4. 나머지 메서드도 재정의 및 테스
트
• Class RigidMountainBike …
def price
def off_road_ability
end
Class FrontSuspensionMountainBike …
def price
def off_road_ability
end
Class FullSuspensionMountainBike …
def price
def off_road_ability
end
module MountainBike …
def price
def off_road_ability
5. 모듈 제거
• module MountainBike …
def price
def off_road_ability
end
타입 코드를 모듈 확장으로 전환
263p
준비
• 모듈 확장(Module Extension)이란?
– Class에 모듈을 붙여 기능을 확장시키는 것
– C/C++에서는 지원하지 않음
동기
• 조건문 제거
• 객체의 타입 동적 전환
– 타입 코드를 재정의로 전환에서는 불가능
• 모듈에서 확장될 클래스의 멤버 변수 접근
가능(편리)
• # 객체의 타입 동적 전환이 가능하다고는
하지만 자유롭지는 못하다
– 확장이 되면 축소하기가 복잡함
1. 타입 코드에 필드 자체 캡슐화
실시
• 234p
2. 타입에 맞는 모듈 작성 및 타입 변경
에 따른 모듈 확장, 원본 클래스는 기본
기능을 반환하도록 수정
• Module FrontSuspensionMountainBike
def price
end
Module FullSuspensionMountainBike
def price
end
class MountainBike …
def type_code=(value)
@type_code = value
case type_code
when :front_suspension: extend(FrontSuspensionMountainBike)
when :full_suspension: extend(FullSuspensionMountainBike)
end
def price
# return rigid_price…
end
end
3. 나머지 메서드도 재정의 및 테스
트
• Module FrontSuspensionMountainBike
def price
def off_road_ability
end
Module FullSuspensionMountainBike
def price
def off_road_ability
end
class MountainBike …
def type_code=(value)
@type_code = value
case type_code
when :front_suspension: extend(FrontSuspensionMountainBike)
when :full_suspension: extend(FullSuspensionMountainBike)
end
def price
# return rigid_price…
end
def off_road_ability
# return rigid road ability…
end
end
4. 타입 코드 대신 모듈을 전달
(before)
• Def type_code=(value)
@type_code = value
case type_code … # extend module
• Bike = MountainBike.new
Bike.type_code = :front_suspension
…
4. 타입 코드 대신 모듈을 전달
(after)
• Def type_code=(mod)
extend(mod)
end
• Bike = MountainBike.new
Bike.type_code =
FrontSuspensionMountainBike
…
타입 코드를 상태/전략 패턴으로
전환
270p
동기
• 조건문 제거
• 타입 코드의 자유로운 동적 전환
1. 타입 코드 필드 자체 캡슐화
• 모듈 확장과 동일
2. 타입에 해당하는 클래스 작성
• Class RigidMountainBike
end
Class FrontSuspensionMountainBike
end
Class FullSuspensionMountainBike
end
3. 타입 코드가 변할 때 해당하는
타입 클래스 생성
• Class MountainBike …
def type_code=(value)
@type_code = value
@bike_type = case type_code
when :rigid: RigidMountainBike.new
when :front_suspension: FrontSuspensionMountainBike.new
when :full_suspension: FullSuspensionMountainbike.new
end
end
4. 하나의 메서드를 선택해 타입 객
체에 위임 및 타입 객체 생성시 필
요한 data 전달• Class RigidMountainBike
def off_road_ability
@tire_width * TIRE_WIDTH_FACTOR
end
end
Class FrontSuspensionMountainBike
def off_road_ability …
end
Class FullSuspensionMountainBike
def off_road_ability …
end
Class MountainBike …
extend Forwardable
def_delegators :@bike_type, :off_road_ability
def type_code(value) ...
… when :rigid: RigidMountainBike.new( :tire_width => @tire_width)
…
end
5. 나머지 메서드도 타입 객체에 위
임
• Class RigidMountainBike
def off_road_ability
def price
end
Class FrontSuspensionMountainBike
def off_road_ability
def price
end
Class FullSuspensionMountainBike
def off_road_ability
def price
end
Class MountainBike …
extend Forwardable
def_delegators :@bike_type, :off_road_ability, :price
end
6. 타입 코드 대신 타입 객체로 생
성
• Bike =
MountainBike.new(FrontSuspensionMountai
nBike.new(
:tire_width => @tire_width,
:front_fork_travel => @front_fork_travel,
… ))
• Class MountainBike …
def initialize(bike_type)
@bike_type = bike_type
end
end
7. 기타(upgradable parameters)
• Class RigidMountainBike…
def upgradable_parameters {
:tire_width => @tire_width,
:base_price => @base_price,
…
}
end
Class FrontSuspensionMountainBike …
def upgradable_parameters { … }
…
• Class MountainBike…
def add_front_suspension(params)
@bike_type =
FrontSuspensionMountainBike.new(
@bike_type.upgradable_parameters.merge(params)
end
end
하위클래스를 필드로 전환
283p
동기
• 하위 클래스가 상수 메서드만 정의
– 상속 구조가 복잡도를 증가시킴
리펙토링 전
• Class Person…
end
class Female < Person
def female?
true
end
def code
‘F’
end
end
class Male < Person
def female?
false
end
def code?
‘M’
end
end
1. 생성자를 팩토리 메서드로 전환
• Class Person
def self.create_female
Female.new
end
def self.create_male
Male.new
end
end
2. 호출 코드를 팩토리 메서드로 전
환
• Scott = Male.new
• -> Scott = Person.create_male
3. 상위클래스에 필드 추가 및 하위
클래스 초기화시 필드 초기화
• Class Person …
def initialize( female, code )
@female = female
@code = code
end
Class Female …
def initialize
super( true, ‘F’ )
end
Class Male …
def initialize
super( false, ‘M’ )
end
4.팩토리 메서드에 초기화 메서드
내용 직접 삽입 및 하위클래스 제거
• Person …
Def self.create_female
Person.new(true, ‘F’)
end
Def self.create_male
Person.new(false, ‘M’)
end
end
• Class male < Person …
Class female < Person …
속성 초기화를 사용시로 미루기
287p
동기
• 가독성
– 초기화 메서드가 복잡한 경우 초기화 로직을
분리
방법1. ||= 사용하기(before)
• Class Employee
attr_reader :emails, :voice_mails
def initialize
@emails = []
@voice_mails = []
end
end
방법1. ||= 사용하기(after)
• Class Employee
def emails
@emails ||= []
end
def voice_mails
@voice_mails ||= []
end
end
방법2. instance_variable_defined?
사용하기(before)
• 이유
– 속성에게 nil이나 false가 “유효한” 값이라면 1
번 방법을 사용할 수 없다.
• Class Employee
def initialize
@assistant = Employee.find_by_boss_id( self.id )
end
end
방법2. instance_variable_defined?
사용하기(after)
• Class Employee
def assistant
unless instance_variable_defined? :@assistant
@assistant = Employee.find_by_boss_id( self.id )
end
end
속성 초기화를 생성 시로 당기기
290p
동기
• 가독성
– 어떤 사람은 한 곳에 있는게 편하다!;;
방법
• 다시 원래 대로…
• 두 기법 중 하나를 선택하여 일관되게 사
용하는 것이 중요
감사합니다

More Related Content

PDF
스위프트 성능 이해하기
PPTX
[스프링 스터디 3일차] @MVC
PDF
생체 광학 데이터 분석 AI 경진대회 1위 수상작
PDF
Legacy code refactoring video rental system
PDF
Template at c++
PPTX
Inheritance
PPTX
딥러닝(Deep Learing) using DeepDetect
PPTX
ECMAScript 6의 새로운 것들!
스위프트 성능 이해하기
[스프링 스터디 3일차] @MVC
생체 광학 데이터 분석 AI 경진대회 1위 수상작
Legacy code refactoring video rental system
Template at c++
Inheritance
딥러닝(Deep Learing) using DeepDetect
ECMAScript 6의 새로운 것들!

Similar to Refactoring with Ruby (리펙토링 루비) (20)

PDF
Django admin site 커스텀하여 적극적으로 활용하기
PPTX
MySQL_MariaDB-성능개선-202201.pptx
PPTX
Refactoring
PPTX
Refactoring
PDF
[15]Android Kotlin을 통한 개발 전략
PPTX
Effective c++ 정리 chapter 6
PDF
PySpark 배우기 Ch 06. ML 패키지 소개하기
PDF
Java class
PDF
Java_05 class
PPTX
Chapter7~9 ppt
PPTX
Effective c++ 정리 chapter 4
PPTX
Hibernate 기초
PPTX
Scala, Spring-Boot, JPA의 불편하면서도 즐거운 동거
PDF
Django를 Django답게, Django로 뉴스 사이트 만들기
PPT
자바야 놀자 PPT
PDF
파이썬 유용한 라이브러리
PPTX
Java, android 스터티4
PPTX
Java, android 스터티4
PPTX
PPTX
Effective C++ Chaper 1
Django admin site 커스텀하여 적극적으로 활용하기
MySQL_MariaDB-성능개선-202201.pptx
Refactoring
Refactoring
[15]Android Kotlin을 통한 개발 전략
Effective c++ 정리 chapter 6
PySpark 배우기 Ch 06. ML 패키지 소개하기
Java class
Java_05 class
Chapter7~9 ppt
Effective c++ 정리 chapter 4
Hibernate 기초
Scala, Spring-Boot, JPA의 불편하면서도 즐거운 동거
Django를 Django답게, Django로 뉴스 사이트 만들기
자바야 놀자 PPT
파이썬 유용한 라이브러리
Java, android 스터티4
Java, android 스터티4
Effective C++ Chaper 1
Ad

Refactoring with Ruby (리펙토링 루비)

  • 3. 준비 • 컬렉션이란? – 1개 이상의 엘리먼트들을 담는 자료 구조 – C++ : 컨테이너 • Vector, map, set, hash_map, … • 컬렉션 캡슐화란? – 컬렉션을 캡슐화 하여 직접 읽거나 쓰지 않고 컬렉션 접근 메소드를 통해 읽거나 쓰도록 하 는 것.
  • 4. 동기 • 의도되지 않은 자료 조작을 방지 – Add/Remove할 때 추가적인 처리가 필요한 경우 – 상황에 따라 Add/Remove등의 조작을 제한하 고 싶을 경우 – … • 내부 데이터 구조 노출 – 구조 변경이 어려워짐 – 클라이언트에게 불필요한 인식을 요구
  • 5. 1. add/remove메소드 추가 • def add_course(course) @courses << course end def remove_course(course) @courses.delete(course) end
  • 6. 2. 컬렉션 초기화 • Def initialize @courses = [] end
  • 7. 3. Setter 호출 부분 수정(before) • kent = Person.new • courses = [] • courses << Course.new("Smalltalk Programming", false) • courses << Course.new("Appreciating Single Malts", true) • kent.course =courses
  • 8. 3. Setter 호출 부분 수정(after) • kent = Person.new • Kent.add_course(Course.new("Smalltalk Programming", false)) • Kent.add_course(Course.new("Appreciatin g Single Malts", true))
  • 9. 4. Getter가 사본을 리턴하도록 수 정 • Def courses @courses.dup end • Def courses @courses.class end
  • 10. 5. 컬렉션을 사용하는 객체로 들어 가야 할 기능 추출 및 이동(before) • number_of_advanced_courses = kent.courses.select do |course| course.advanced? end.size
  • 11. 5. 컬렉션을 사용하는 객체로 들어 가야 할 기능 추출 및 이동(after1) • def number_of_advanced_courses kent.courses.select { |course| course.advanced? }.size end
  • 12. 5. 컬렉션을 사용하는 객체로 들어 가야 할 기능 추출 및 이동(after2) • def number_of_advanced_courses @courses.select { |course| course.advanced? }.size end • Def number_of_courses @courses.size end
  • 14. 준비 • 레코드란? – 일련의 기록 • 덤 데이터 객체(dumb data object)란? – 데이터만 존재하는 클래스(구조체) – 데이터와 인터페이스가 함께 존재해야 한다는 객체 지향의 원칙과는 맞지 않음
  • 15. 동기 • 레코드 구조를 “가독성 있고 편리하게” 인터페이 싱 할 수 있도록 해줌 – 배열을 객체로 전환과 비슷 • Struct UserData • { – Name – Age – Email • } • Open회원가입Dialog(UserData data)
  • 16. 방법 • 레코드를 나타낼 클래스를 작성 • 클래스에 필드 추가 및 필요한 항목에 대 한 접근 메서드 작성 • 끝 – 나머진 후에 나옴.( 다른 분께서… )
  • 18. 준비 • 타입 코드란? – 흔히 말하는 enum • BikeType_Rigid BikeType_FrontSuspension BikeType_FullSuspension • 재정의란? – 타입별 기능에 대한 재정의를 말함(원서 225p)
  • 19. 동기 • 조건문 제거 – 코드의 복잡성을 낮춤
  • 20. 1. 타입에 해당하는 클래스 작성 및 기본 클래스를 모듈로 변환 • Class RigidMountainBike include MoutainBike end Class FrontSuspensionMountainBike include MountainBike end Class FullSuspensionMountainBike include MountainBike end Class module MountainBike ….
  • 21. 2. 원본 클래스 생성을 원하는 타입 의 클래스 생성으로 대체 및 테스트 • Bike = MountainBike.new(…) • -> Bike = FrontSuspensionMountainBike.new(…)
  • 22. 3. 타입 코드에 의존적인 메서드 중 하 나를 각 타입 클래스에 맞게 재정의 및 테스트 • Class RigidMountainBike … def price end Class FrontSuspensionMountainBike … def price end Class FullSuspensionMountainBike … def price end module MountainBike … def price
  • 23. 4. 나머지 메서드도 재정의 및 테스 트 • Class RigidMountainBike … def price def off_road_ability end Class FrontSuspensionMountainBike … def price def off_road_ability end Class FullSuspensionMountainBike … def price def off_road_ability end module MountainBike … def price def off_road_ability
  • 24. 5. 모듈 제거 • module MountainBike … def price def off_road_ability end
  • 25. 타입 코드를 모듈 확장으로 전환 263p
  • 26. 준비 • 모듈 확장(Module Extension)이란? – Class에 모듈을 붙여 기능을 확장시키는 것 – C/C++에서는 지원하지 않음
  • 27. 동기 • 조건문 제거 • 객체의 타입 동적 전환 – 타입 코드를 재정의로 전환에서는 불가능 • 모듈에서 확장될 클래스의 멤버 변수 접근 가능(편리) • # 객체의 타입 동적 전환이 가능하다고는 하지만 자유롭지는 못하다 – 확장이 되면 축소하기가 복잡함
  • 28. 1. 타입 코드에 필드 자체 캡슐화 실시 • 234p
  • 29. 2. 타입에 맞는 모듈 작성 및 타입 변경 에 따른 모듈 확장, 원본 클래스는 기본 기능을 반환하도록 수정 • Module FrontSuspensionMountainBike def price end Module FullSuspensionMountainBike def price end class MountainBike … def type_code=(value) @type_code = value case type_code when :front_suspension: extend(FrontSuspensionMountainBike) when :full_suspension: extend(FullSuspensionMountainBike) end def price # return rigid_price… end end
  • 30. 3. 나머지 메서드도 재정의 및 테스 트 • Module FrontSuspensionMountainBike def price def off_road_ability end Module FullSuspensionMountainBike def price def off_road_ability end class MountainBike … def type_code=(value) @type_code = value case type_code when :front_suspension: extend(FrontSuspensionMountainBike) when :full_suspension: extend(FullSuspensionMountainBike) end def price # return rigid_price… end def off_road_ability # return rigid road ability… end end
  • 31. 4. 타입 코드 대신 모듈을 전달 (before) • Def type_code=(value) @type_code = value case type_code … # extend module • Bike = MountainBike.new Bike.type_code = :front_suspension …
  • 32. 4. 타입 코드 대신 모듈을 전달 (after) • Def type_code=(mod) extend(mod) end • Bike = MountainBike.new Bike.type_code = FrontSuspensionMountainBike …
  • 33. 타입 코드를 상태/전략 패턴으로 전환 270p
  • 34. 동기 • 조건문 제거 • 타입 코드의 자유로운 동적 전환
  • 35. 1. 타입 코드 필드 자체 캡슐화 • 모듈 확장과 동일
  • 36. 2. 타입에 해당하는 클래스 작성 • Class RigidMountainBike end Class FrontSuspensionMountainBike end Class FullSuspensionMountainBike end
  • 37. 3. 타입 코드가 변할 때 해당하는 타입 클래스 생성 • Class MountainBike … def type_code=(value) @type_code = value @bike_type = case type_code when :rigid: RigidMountainBike.new when :front_suspension: FrontSuspensionMountainBike.new when :full_suspension: FullSuspensionMountainbike.new end end
  • 38. 4. 하나의 메서드를 선택해 타입 객 체에 위임 및 타입 객체 생성시 필 요한 data 전달• Class RigidMountainBike def off_road_ability @tire_width * TIRE_WIDTH_FACTOR end end Class FrontSuspensionMountainBike def off_road_ability … end Class FullSuspensionMountainBike def off_road_ability … end Class MountainBike … extend Forwardable def_delegators :@bike_type, :off_road_ability def type_code(value) ... … when :rigid: RigidMountainBike.new( :tire_width => @tire_width) … end
  • 39. 5. 나머지 메서드도 타입 객체에 위 임 • Class RigidMountainBike def off_road_ability def price end Class FrontSuspensionMountainBike def off_road_ability def price end Class FullSuspensionMountainBike def off_road_ability def price end Class MountainBike … extend Forwardable def_delegators :@bike_type, :off_road_ability, :price end
  • 40. 6. 타입 코드 대신 타입 객체로 생 성 • Bike = MountainBike.new(FrontSuspensionMountai nBike.new( :tire_width => @tire_width, :front_fork_travel => @front_fork_travel, … )) • Class MountainBike … def initialize(bike_type) @bike_type = bike_type end end
  • 41. 7. 기타(upgradable parameters) • Class RigidMountainBike… def upgradable_parameters { :tire_width => @tire_width, :base_price => @base_price, … } end Class FrontSuspensionMountainBike … def upgradable_parameters { … } … • Class MountainBike… def add_front_suspension(params) @bike_type = FrontSuspensionMountainBike.new( @bike_type.upgradable_parameters.merge(params) end end
  • 43. 동기 • 하위 클래스가 상수 메서드만 정의 – 상속 구조가 복잡도를 증가시킴
  • 44. 리펙토링 전 • Class Person… end class Female < Person def female? true end def code ‘F’ end end class Male < Person def female? false end def code? ‘M’ end end
  • 45. 1. 생성자를 팩토리 메서드로 전환 • Class Person def self.create_female Female.new end def self.create_male Male.new end end
  • 46. 2. 호출 코드를 팩토리 메서드로 전 환 • Scott = Male.new • -> Scott = Person.create_male
  • 47. 3. 상위클래스에 필드 추가 및 하위 클래스 초기화시 필드 초기화 • Class Person … def initialize( female, code ) @female = female @code = code end Class Female … def initialize super( true, ‘F’ ) end Class Male … def initialize super( false, ‘M’ ) end
  • 48. 4.팩토리 메서드에 초기화 메서드 내용 직접 삽입 및 하위클래스 제거 • Person … Def self.create_female Person.new(true, ‘F’) end Def self.create_male Person.new(false, ‘M’) end end • Class male < Person … Class female < Person …
  • 50. 동기 • 가독성 – 초기화 메서드가 복잡한 경우 초기화 로직을 분리
  • 51. 방법1. ||= 사용하기(before) • Class Employee attr_reader :emails, :voice_mails def initialize @emails = [] @voice_mails = [] end end
  • 52. 방법1. ||= 사용하기(after) • Class Employee def emails @emails ||= [] end def voice_mails @voice_mails ||= [] end end
  • 53. 방법2. instance_variable_defined? 사용하기(before) • 이유 – 속성에게 nil이나 false가 “유효한” 값이라면 1 번 방법을 사용할 수 없다. • Class Employee def initialize @assistant = Employee.find_by_boss_id( self.id ) end end
  • 54. 방법2. instance_variable_defined? 사용하기(after) • Class Employee def assistant unless instance_variable_defined? :@assistant @assistant = Employee.find_by_boss_id( self.id ) end end
  • 55. 속성 초기화를 생성 시로 당기기 290p
  • 56. 동기 • 가독성 – 어떤 사람은 한 곳에 있는게 편하다!;;
  • 57. 방법 • 다시 원래 대로… • 두 기법 중 하나를 선택하여 일관되게 사 용하는 것이 중요