refactoring with ruby (리펙토링 루비)
TRANSCRIPT
![Page 1: Refactoring with Ruby (리펙토링 루비)](https://reader034.vdocuments.net/reader034/viewer/2022042611/589df28e1a28ab773b8b73d5/html5/thumbnails/1.jpg)
리펙토링 루비4 번째 발표
![Page 2: Refactoring with Ruby (리펙토링 루비)](https://reader034.vdocuments.net/reader034/viewer/2022042611/589df28e1a28ab773b8b73d5/html5/thumbnails/2.jpg)
컬렉션 캡슐화248p
![Page 3: Refactoring with Ruby (리펙토링 루비)](https://reader034.vdocuments.net/reader034/viewer/2022042611/589df28e1a28ab773b8b73d5/html5/thumbnails/3.jpg)
준비• 컬렉션이란 ?– 1 개 이상의 엘리먼트들을 담는 자료 구조– C++ : 컨테이너• Vector, map, set, hash_map, …
• 컬렉션 캡슐화란 ?–컬렉션을 캡슐화 하여 직접 읽거나 쓰지 않고 컬렉션 접근 메소드를 통해 읽거나 쓰도록 하는 것 .
![Page 4: Refactoring with Ruby (리펙토링 루비)](https://reader034.vdocuments.net/reader034/viewer/2022042611/589df28e1a28ab773b8b73d5/html5/thumbnails/4.jpg)
동기• 의도되지 않은 자료 조작을 방지– Add/Remove 할 때 추가적인 처리가 필요한 경우–상황에 따라 Add/Remove 등의 조작을 제한하고 싶을 경우–…
• 내부 데이터 구조 노출–구조 변경이 어려워짐–클라이언트에게 불필요한 인식을 요구
![Page 5: Refactoring with Ruby (리펙토링 루비)](https://reader034.vdocuments.net/reader034/viewer/2022042611/589df28e1a28ab773b8b73d5/html5/thumbnails/5.jpg)
1. add/remove 메소드 추가• def add_course(course)
@courses << courseend
def remove_course(course)@courses.delete(course)
end
![Page 6: Refactoring with Ruby (리펙토링 루비)](https://reader034.vdocuments.net/reader034/viewer/2022042611/589df28e1a28ab773b8b73d5/html5/thumbnails/6.jpg)
2. 컬렉션 초기화• Def initialize
@courses = []end
![Page 7: Refactoring with Ruby (리펙토링 루비)](https://reader034.vdocuments.net/reader034/viewer/2022042611/589df28e1a28ab773b8b73d5/html5/thumbnails/7.jpg)
3. Setter 호출 부분 수정(before)
• kent = Person.new• courses = []• courses << Course.new("Smalltalk
Programming", false)• courses <<
Course.new("Appreciating Single Malts", true)
• kent.course =courses
![Page 8: Refactoring with Ruby (리펙토링 루비)](https://reader034.vdocuments.net/reader034/viewer/2022042611/589df28e1a28ab773b8b73d5/html5/thumbnails/8.jpg)
3. Setter 호출 부분 수정 (after)• kent = Person.new• Kent.add_course(Course.new("Smallt
alk Programming", false))• Kent.add_course(Course.new("Apprec
iating Single Malts", true))
![Page 10: Refactoring with Ruby (리펙토링 루비)](https://reader034.vdocuments.net/reader034/viewer/2022042611/589df28e1a28ab773b8b73d5/html5/thumbnails/10.jpg)
5. 컬렉션을 사용하는 객체로 들어가야 할 기능 추출 및 이동 (before)• number_of_advanced_courses = ken-
t.courses.select do |course|course.advanced?
end.size
![Page 11: Refactoring with Ruby (리펙토링 루비)](https://reader034.vdocuments.net/reader034/viewer/2022042611/589df28e1a28ab773b8b73d5/html5/thumbnails/11.jpg)
5. 컬렉션을 사용하는 객체로 들어가야 할 기능 추출 및 이동 (after1)• def number_of_advanced_courses
kent.courses.select { |course| course.advanced? }.size
end
![Page 12: Refactoring with Ruby (리펙토링 루비)](https://reader034.vdocuments.net/reader034/viewer/2022042611/589df28e1a28ab773b8b73d5/html5/thumbnails/12.jpg)
5. 컬렉션을 사용하는 객체로 들어가야 할 기능 추출 및 이동 (after2)• def number_of_advanced_courses
@courses.select { |course| course.advanced? }.size
end• Def number_of_courses
@courses.sizeend
![Page 13: Refactoring with Ruby (리펙토링 루비)](https://reader034.vdocuments.net/reader034/viewer/2022042611/589df28e1a28ab773b8b73d5/html5/thumbnails/13.jpg)
레코드를 데이터 클래스로 전환254p
![Page 14: Refactoring with Ruby (리펙토링 루비)](https://reader034.vdocuments.net/reader034/viewer/2022042611/589df28e1a28ab773b8b73d5/html5/thumbnails/14.jpg)
준비• 레코드란 ?–일련의 기록
• 덤 데이터 객체 (dumb data object) 란 ?–데이터만 존재하는 클래스 ( 구조체 )–데이터와 인터페이스가 함께 존재해야 한다는 객체 지향의 원칙과는 맞지 않음
![Page 15: Refactoring with Ruby (리펙토링 루비)](https://reader034.vdocuments.net/reader034/viewer/2022042611/589df28e1a28ab773b8b73d5/html5/thumbnails/15.jpg)
동기• 레코드 구조를 “가독성 있고 편리하게” 인터페이싱 할 수 있도록 해줌
– 배열을 객체로 전환과 비슷• Struct UserData• {
– Name– Age– Email
• }• Open 회원가입 Dialog(UserData data)
![Page 16: Refactoring with Ruby (리펙토링 루비)](https://reader034.vdocuments.net/reader034/viewer/2022042611/589df28e1a28ab773b8b73d5/html5/thumbnails/16.jpg)
방법• 레코드를 나타낼 클래스를 작성• 클래스에 필드 추가 및 필요한 항목에 대한 접근 메서드 작성• 끝–나머진 후에 나옴 .( 다른 분께서… )
![Page 17: Refactoring with Ruby (리펙토링 루비)](https://reader034.vdocuments.net/reader034/viewer/2022042611/589df28e1a28ab773b8b73d5/html5/thumbnails/17.jpg)
타입 코드를 재정의로 전환255p
![Page 18: Refactoring with Ruby (리펙토링 루비)](https://reader034.vdocuments.net/reader034/viewer/2022042611/589df28e1a28ab773b8b73d5/html5/thumbnails/18.jpg)
준비• 타입 코드란 ?–흔히 말하는 enum• BikeType_Rigid
BikeType_FrontSuspensionBikeType_FullSuspension
• 재정의란 ?–타입별 기능에 대한 재정의를 말함 ( 원서 225p)
![Page 19: Refactoring with Ruby (리펙토링 루비)](https://reader034.vdocuments.net/reader034/viewer/2022042611/589df28e1a28ab773b8b73d5/html5/thumbnails/19.jpg)
동기• 조건문 제거–코드의 복잡성을 낮춤
![Page 20: Refactoring with Ruby (리펙토링 루비)](https://reader034.vdocuments.net/reader034/viewer/2022042611/589df28e1a28ab773b8b73d5/html5/thumbnails/20.jpg)
1. 타입에 해당하는 클래스 작성 및 기본 클래스를 모듈로 변환• Class RigidMountainBike
include MoutainBikeend
Class FrontSuspensionMountainBikeinclude MountainBike
end
Class FullSuspensionMountainBike include MountainBike
end
Class module MountainBike….
![Page 21: Refactoring with Ruby (리펙토링 루비)](https://reader034.vdocuments.net/reader034/viewer/2022042611/589df28e1a28ab773b8b73d5/html5/thumbnails/21.jpg)
2. 원본 클래스 생성을 원하는 타입의 클래스 생성으로 대체 및 테스트• Bike = MountainBike.new(…)• -> Bike = FrontSuspensionMountain-
Bike.new(…)
![Page 22: Refactoring with Ruby (리펙토링 루비)](https://reader034.vdocuments.net/reader034/viewer/2022042611/589df28e1a28ab773b8b73d5/html5/thumbnails/22.jpg)
3. 타입 코드에 의존적인 메서드 중 하나를 각 타입 클래스에 맞게 재정의 및 테스트• Class RigidMountainBike …
def priceend
Class FrontSuspensionMountainBike …def price
end
Class FullSuspensionMountainBike …def price
end
module MountainBike …def price
![Page 23: Refactoring with Ruby (리펙토링 루비)](https://reader034.vdocuments.net/reader034/viewer/2022042611/589df28e1a28ab773b8b73d5/html5/thumbnails/23.jpg)
4. 나머지 메서드도 재정의 및 테스트• Class RigidMountainBike …
def pricedef off_road_ability
end
Class FrontSuspensionMountainBike …def pricedef off_road_ability
end
Class FullSuspensionMountainBike …def pricedef off_road_ability
end
module MountainBike …def pricedef off_road_ability
![Page 24: Refactoring with Ruby (리펙토링 루비)](https://reader034.vdocuments.net/reader034/viewer/2022042611/589df28e1a28ab773b8b73d5/html5/thumbnails/24.jpg)
5. 모듈 제거• module MountainBike …
def pricedef off_road_ability
end
![Page 25: Refactoring with Ruby (리펙토링 루비)](https://reader034.vdocuments.net/reader034/viewer/2022042611/589df28e1a28ab773b8b73d5/html5/thumbnails/25.jpg)
타입 코드를 모듈 확장으로 전환263p
![Page 26: Refactoring with Ruby (리펙토링 루비)](https://reader034.vdocuments.net/reader034/viewer/2022042611/589df28e1a28ab773b8b73d5/html5/thumbnails/26.jpg)
준비• 모듈 확장 (Module Extension) 이란 ?– Class 에 모듈을 붙여 기능을 확장시키는 것– C/C++ 에서는 지원하지 않음
![Page 27: Refactoring with Ruby (리펙토링 루비)](https://reader034.vdocuments.net/reader034/viewer/2022042611/589df28e1a28ab773b8b73d5/html5/thumbnails/27.jpg)
동기• 조건문 제거• 객체의 타입 동적 전환–타입 코드를 재정의로 전환에서는 불가능
• 모듈에서 확장될 클래스의 멤버 변수 접근 가능 ( 편리 )• # 객체의 타입 동적 전환이 가능하다고는 하지만 자유롭지는 못하다–확장이 되면 축소하기가 복잡함
![Page 28: Refactoring with Ruby (리펙토링 루비)](https://reader034.vdocuments.net/reader034/viewer/2022042611/589df28e1a28ab773b8b73d5/html5/thumbnails/28.jpg)
1. 타입 코드에 필드 자체 캡슐화 실시• 234p
![Page 29: Refactoring with Ruby (리펙토링 루비)](https://reader034.vdocuments.net/reader034/viewer/2022042611/589df28e1a28ab773b8b73d5/html5/thumbnails/29.jpg)
2. 타입에 맞는 모듈 작성 및 타입 변경에 따른 모듈 확장 , 원본 클래스는 기본 기능을 반환하도록 수정• Module FrontSuspensionMountainBike
def priceend
Module FullSuspensionMountainBikedef price
end
class MountainBike …def type_code=(value)
@type_code = valuecase type_code
when :front_suspension: extend(FrontSuspensionMountainBike)
when :full_suspension: extend(FullSuspensionMountainBike)end
def price# return rigid_price…
endend
![Page 30: Refactoring with Ruby (리펙토링 루비)](https://reader034.vdocuments.net/reader034/viewer/2022042611/589df28e1a28ab773b8b73d5/html5/thumbnails/30.jpg)
3. 나머지 메서드도 재정의 및 테스트• Module FrontSuspensionMountainBike
def pricedef off_road_ability
end
Module FullSuspensionMountainBikedef pricedef off_road_ability
end
class MountainBike …def type_code=(value)@type_code = valuecase type_codewhen :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
![Page 31: Refactoring with Ruby (리펙토링 루비)](https://reader034.vdocuments.net/reader034/viewer/2022042611/589df28e1a28ab773b8b73d5/html5/thumbnails/31.jpg)
4. 타입 코드 대신 모듈을 전달 (before)
• Def type_code=(value)@type_code = valuecase type_code … # extend
module
• Bike = MountainBike.newBike.type_code = :front_suspension…
![Page 32: Refactoring with Ruby (리펙토링 루비)](https://reader034.vdocuments.net/reader034/viewer/2022042611/589df28e1a28ab773b8b73d5/html5/thumbnails/32.jpg)
4. 타입 코드 대신 모듈을 전달 (after)• Def type_code=(mod)
extend(mod)end
• Bike = MountainBike.newBike.type_code =
FrontSuspensionMountainBike…
![Page 33: Refactoring with Ruby (리펙토링 루비)](https://reader034.vdocuments.net/reader034/viewer/2022042611/589df28e1a28ab773b8b73d5/html5/thumbnails/33.jpg)
타입 코드를 상태 / 전략 패턴으로 전환270p
![Page 34: Refactoring with Ruby (리펙토링 루비)](https://reader034.vdocuments.net/reader034/viewer/2022042611/589df28e1a28ab773b8b73d5/html5/thumbnails/34.jpg)
동기• 조건문 제거• 타입 코드의 자유로운 동적 전환
![Page 35: Refactoring with Ruby (리펙토링 루비)](https://reader034.vdocuments.net/reader034/viewer/2022042611/589df28e1a28ab773b8b73d5/html5/thumbnails/35.jpg)
1. 타입 코드 필드 자체 캡슐화• 모듈 확장과 동일
![Page 36: Refactoring with Ruby (리펙토링 루비)](https://reader034.vdocuments.net/reader034/viewer/2022042611/589df28e1a28ab773b8b73d5/html5/thumbnails/36.jpg)
2. 타입에 해당하는 클래스 작성• Class RigidMountainBike
end
Class FrontSuspensionMountainBikeend
Class FullSuspensionMountainBikeend
![Page 37: Refactoring with Ruby (리펙토링 루비)](https://reader034.vdocuments.net/reader034/viewer/2022042611/589df28e1a28ab773b8b73d5/html5/thumbnails/37.jpg)
3. 타입 코드가 변할 때 해당하는 타입 클래스 생성• Class MountainBike …
def type_code=(value)@type_code = value@bike_type = case type_code
when :rigid: RigidMountainBike.newwhen :front_suspension: FrontSuspensionMoun-
tainBike.newwhen :full_suspension: FullSuspensionMountain-
bike.newend
end
![Page 38: Refactoring with Ruby (리펙토링 루비)](https://reader034.vdocuments.net/reader034/viewer/2022042611/589df28e1a28ab773b8b73d5/html5/thumbnails/38.jpg)
4. 하나의 메서드를 선택해 타입 객체에 위임 및 타입 객체 생성시 필요한 data 전달• Class RigidMountainBike
def off_road_ability@tire_width * TIRE_WIDTH_FACTORend
end
Class FrontSuspensionMountainBikedef off_road_ability …
end
Class FullSuspensionMountainBikedef off_road_ability …
end
Class MountainBike …
extend Forwardabledef_delegators :@bike_type, :off_road_ability
def type_code(value) ...… when :rigid: RigidMountainBike.new( :tire_width => @tire_width)…
end
![Page 39: Refactoring with Ruby (리펙토링 루비)](https://reader034.vdocuments.net/reader034/viewer/2022042611/589df28e1a28ab773b8b73d5/html5/thumbnails/39.jpg)
5. 나머지 메서드도 타입 객체에 위임• Class RigidMountainBike
def off_road_abilitydef price
end
Class FrontSuspensionMountainBikedef off_road_abilitydef price
end
Class FullSuspensionMountainBikedef off_road_abilitydef price
end
Class MountainBike …
extend Forwardabledef_delegators :@bike_type, :off_road_ability, :price
end
![Page 40: Refactoring with Ruby (리펙토링 루비)](https://reader034.vdocuments.net/reader034/viewer/2022042611/589df28e1a28ab773b8b73d5/html5/thumbnails/40.jpg)
6. 타입 코드 대신 타입 객체로 생성• Bike =
MountainBike.new(FrontSuspensionMountainBike.new(
:tire_width => @tire_width,:front_fork_travel => @front_fork_travel,… ))
• Class MountainBike …def initialize(bike_type)
@bike_type = bike_typeend
end
![Page 41: Refactoring with Ruby (리펙토링 루비)](https://reader034.vdocuments.net/reader034/viewer/2022042611/589df28e1a28ab773b8b73d5/html5/thumbnails/41.jpg)
7. 기타 (upgradable parame-ters)
• 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
![Page 42: Refactoring with Ruby (리펙토링 루비)](https://reader034.vdocuments.net/reader034/viewer/2022042611/589df28e1a28ab773b8b73d5/html5/thumbnails/42.jpg)
하위클래스를 필드로 전환283p
![Page 43: Refactoring with Ruby (리펙토링 루비)](https://reader034.vdocuments.net/reader034/viewer/2022042611/589df28e1a28ab773b8b73d5/html5/thumbnails/43.jpg)
동기• 하위 클래스가 상수 메서드만 정의–상속 구조가 복잡도를 증가시킴
![Page 44: Refactoring with Ruby (리펙토링 루비)](https://reader034.vdocuments.net/reader034/viewer/2022042611/589df28e1a28ab773b8b73d5/html5/thumbnails/44.jpg)
리펙토링 전• Class Person…
end
class Female < Persondef female?trueend
def code‘F’end
end
class Male < Persondef female?falseend
def code?‘M’end
end
![Page 45: Refactoring with Ruby (리펙토링 루비)](https://reader034.vdocuments.net/reader034/viewer/2022042611/589df28e1a28ab773b8b73d5/html5/thumbnails/45.jpg)
1. 생성자를 팩토리 메서드로 전환• Class Person
def self.create_femaleFemale.new
end
def self.create_maleMale.new
endend
![Page 46: Refactoring with Ruby (리펙토링 루비)](https://reader034.vdocuments.net/reader034/viewer/2022042611/589df28e1a28ab773b8b73d5/html5/thumbnails/46.jpg)
2. 호출 코드를 팩토리 메서드로 전환• Scott = Male.new• -> Scott = Person.create_male
![Page 47: Refactoring with Ruby (리펙토링 루비)](https://reader034.vdocuments.net/reader034/viewer/2022042611/589df28e1a28ab773b8b73d5/html5/thumbnails/47.jpg)
3. 상위클래스에 필드 추가 및 하위 클래스 초기화시 필드 초기화• Class Person …
def initialize( female, code )@female = female@code = codeend
Class Female …def initializesuper( true, ‘F’ )end
Class Male …def initializesuper( false, ‘M’ )end
![Page 48: Refactoring with Ruby (리펙토링 루비)](https://reader034.vdocuments.net/reader034/viewer/2022042611/589df28e1a28ab773b8b73d5/html5/thumbnails/48.jpg)
4. 팩토리 메서드에 초기화 메서드 내용 직접 삽입 및 하위클래스 제거• Person …
Def self.create_femalePerson.new(true, ‘F’)end
Def self.create_malePerson.new(false, ‘M’)end
end
• Class male < Person …Class female < Person …
![Page 49: Refactoring with Ruby (리펙토링 루비)](https://reader034.vdocuments.net/reader034/viewer/2022042611/589df28e1a28ab773b8b73d5/html5/thumbnails/49.jpg)
속성 초기화를 사용시로 미루기287p
![Page 50: Refactoring with Ruby (리펙토링 루비)](https://reader034.vdocuments.net/reader034/viewer/2022042611/589df28e1a28ab773b8b73d5/html5/thumbnails/50.jpg)
동기• 가독성–초기화 메서드가 복잡한 경우 초기화 로직을 분리
![Page 51: Refactoring with Ruby (리펙토링 루비)](https://reader034.vdocuments.net/reader034/viewer/2022042611/589df28e1a28ab773b8b73d5/html5/thumbnails/51.jpg)
방법 1. ||= 사용하기 (before)• Class Employee
attr_reader :emails, :voice_mails
def initialize@emails = []@voice_mails = []
endend
![Page 52: Refactoring with Ruby (리펙토링 루비)](https://reader034.vdocuments.net/reader034/viewer/2022042611/589df28e1a28ab773b8b73d5/html5/thumbnails/52.jpg)
방법 1. ||= 사용하기 (after)• Class Employee
def emails@emails ||= []
end
def voice_mails@voice_mails ||= []
endend
![Page 53: Refactoring with Ruby (리펙토링 루비)](https://reader034.vdocuments.net/reader034/viewer/2022042611/589df28e1a28ab773b8b73d5/html5/thumbnails/53.jpg)
방법 2. instance_variable_defined? 사용하기 (before)• 이유–속성에게 nil 이나 false 가 “유효한” 값이라면
1 번 방법을 사용할 수 없다 .• Class Employee
def initialize@assistant =
Employee.find_by_boss_id( self.id )end
end
![Page 54: Refactoring with Ruby (리펙토링 루비)](https://reader034.vdocuments.net/reader034/viewer/2022042611/589df28e1a28ab773b8b73d5/html5/thumbnails/54.jpg)
방법 2. instance_variable_defined? 사용하기 (after)• Class Employee
def assistantunless instance_variable_defined? :@assistant
@assistant = Employee.find_by_boss_id( self.id )
endend
![Page 55: Refactoring with Ruby (리펙토링 루비)](https://reader034.vdocuments.net/reader034/viewer/2022042611/589df28e1a28ab773b8b73d5/html5/thumbnails/55.jpg)
속성 초기화를 생성 시로 당기기290p
![Page 56: Refactoring with Ruby (리펙토링 루비)](https://reader034.vdocuments.net/reader034/viewer/2022042611/589df28e1a28ab773b8b73d5/html5/thumbnails/56.jpg)
동기• 가독성–어떤 사람은 한 곳에 있는게 편하다 !;;
![Page 57: Refactoring with Ruby (리펙토링 루비)](https://reader034.vdocuments.net/reader034/viewer/2022042611/589df28e1a28ab773b8b73d5/html5/thumbnails/57.jpg)
방법• 다시 원래 대로…
• 두 기법 중 하나를 선택하여 일관되게 사용하는 것이 중요
![Page 58: Refactoring with Ruby (리펙토링 루비)](https://reader034.vdocuments.net/reader034/viewer/2022042611/589df28e1a28ab773b8b73d5/html5/thumbnails/58.jpg)
감사합니다