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
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
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
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
…
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
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 …