refactoring with ruby (리펙토링 루비)

58
리리리리 리리 4 리리 리리

Upload: hanstar17

Post on 13-Apr-2017

50 views

Category:

Software


3 download

TRANSCRIPT

Page 1: Refactoring with Ruby (리펙토링 루비)

리펙토링 루비4 번째 발표

Page 2: Refactoring with Ruby (리펙토링 루비)

컬렉션 캡슐화248p

Page 3: Refactoring with Ruby (리펙토링 루비)

준비• 컬렉션이란 ?– 1 개 이상의 엘리먼트들을 담는 자료 구조– C++ : 컨테이너• Vector, map, set, hash_map, …

• 컬렉션 캡슐화란 ?–컬렉션을 캡슐화 하여 직접 읽거나 쓰지 않고 컬렉션 접근 메소드를 통해 읽거나 쓰도록 하는 것 .

Page 4: Refactoring with Ruby (리펙토링 루비)

동기• 의도되지 않은 자료 조작을 방지– Add/Remove 할 때 추가적인 처리가 필요한 경우–상황에 따라 Add/Remove 등의 조작을 제한하고 싶을 경우–…

• 내부 데이터 구조 노출–구조 변경이 어려워짐–클라이언트에게 불필요한 인식을 요구

Page 5: Refactoring with Ruby (리펙토링 루비)

1. add/remove 메소드 추가• def add_course(course)

@courses << courseend

def remove_course(course)@courses.delete(course)

end

Page 6: Refactoring with Ruby (리펙토링 루비)

2. 컬렉션 초기화• Def initialize

@courses = []end

Page 7: Refactoring with Ruby (리펙토링 루비)

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 (리펙토링 루비)

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 9: Refactoring with Ruby (리펙토링 루비)

4. Getter 가 사본을 리턴하도록 수정• Def courses

@courses.dupend

• Def [email protected]

end

Page 10: Refactoring with Ruby (리펙토링 루비)

5. 컬렉션을 사용하는 객체로 들어가야 할 기능 추출 및 이동 (before)• number_of_advanced_courses = ken-

t.courses.select do |course|course.advanced?

end.size

Page 11: Refactoring with Ruby (리펙토링 루비)

5. 컬렉션을 사용하는 객체로 들어가야 할 기능 추출 및 이동 (after1)• def number_of_advanced_courses

kent.courses.select { |course| course.advanced? }.size

end

Page 12: Refactoring with Ruby (리펙토링 루비)

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 (리펙토링 루비)

레코드를 데이터 클래스로 전환254p

Page 14: Refactoring with Ruby (리펙토링 루비)

준비• 레코드란 ?–일련의 기록

• 덤 데이터 객체 (dumb data object) 란 ?–데이터만 존재하는 클래스 ( 구조체 )–데이터와 인터페이스가 함께 존재해야 한다는 객체 지향의 원칙과는 맞지 않음

Page 15: Refactoring with Ruby (리펙토링 루비)

동기• 레코드 구조를 “가독성 있고 편리하게” 인터페이싱 할 수 있도록 해줌

– 배열을 객체로 전환과 비슷• Struct UserData• {

– Name– Age– Email

• }• Open 회원가입 Dialog(UserData data)

Page 16: Refactoring with Ruby (리펙토링 루비)

방법• 레코드를 나타낼 클래스를 작성• 클래스에 필드 추가 및 필요한 항목에 대한 접근 메서드 작성• 끝–나머진 후에 나옴 .( 다른 분께서… )

Page 17: Refactoring with Ruby (리펙토링 루비)

타입 코드를 재정의로 전환255p

Page 18: Refactoring with Ruby (리펙토링 루비)

준비• 타입 코드란 ?–흔히 말하는 enum• BikeType_Rigid

BikeType_FrontSuspensionBikeType_FullSuspension

• 재정의란 ?–타입별 기능에 대한 재정의를 말함 ( 원서 225p)

Page 19: Refactoring with Ruby (리펙토링 루비)

동기• 조건문 제거–코드의 복잡성을 낮춤

Page 20: Refactoring with Ruby (리펙토링 루비)

1. 타입에 해당하는 클래스 작성 및 기본 클래스를 모듈로 변환• Class RigidMountainBike

include MoutainBikeend

Class FrontSuspensionMountainBikeinclude MountainBike

end

Class FullSuspensionMountainBike include MountainBike

end

Class module MountainBike….

Page 21: Refactoring with Ruby (리펙토링 루비)

2. 원본 클래스 생성을 원하는 타입의 클래스 생성으로 대체 및 테스트• Bike = MountainBike.new(…)• -> Bike = FrontSuspensionMountain-

Bike.new(…)

Page 22: Refactoring with Ruby (리펙토링 루비)

3. 타입 코드에 의존적인 메서드 중 하나를 각 타입 클래스에 맞게 재정의 및 테스트• Class RigidMountainBike …

def priceend

Class FrontSuspensionMountainBike …def price

end

Class FullSuspensionMountainBike …def price

end

module MountainBike …def price

Page 23: Refactoring with Ruby (리펙토링 루비)

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 (리펙토링 루비)

5. 모듈 제거• module MountainBike …

def pricedef off_road_ability

end

Page 25: Refactoring with Ruby (리펙토링 루비)

타입 코드를 모듈 확장으로 전환263p

Page 26: Refactoring with Ruby (리펙토링 루비)

준비• 모듈 확장 (Module Extension) 이란 ?– Class 에 모듈을 붙여 기능을 확장시키는 것– C/C++ 에서는 지원하지 않음

Page 27: Refactoring with Ruby (리펙토링 루비)

동기• 조건문 제거• 객체의 타입 동적 전환–타입 코드를 재정의로 전환에서는 불가능

• 모듈에서 확장될 클래스의 멤버 변수 접근 가능 ( 편리 )• # 객체의 타입 동적 전환이 가능하다고는 하지만 자유롭지는 못하다–확장이 되면 축소하기가 복잡함

Page 28: Refactoring with Ruby (리펙토링 루비)

1. 타입 코드에 필드 자체 캡슐화 실시• 234p

Page 29: Refactoring with Ruby (리펙토링 루비)

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 (리펙토링 루비)

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 (리펙토링 루비)

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 (리펙토링 루비)

4. 타입 코드 대신 모듈을 전달 (after)• Def type_code=(mod)

extend(mod)end

• Bike = MountainBike.newBike.type_code =

FrontSuspensionMountainBike…

Page 33: Refactoring with Ruby (리펙토링 루비)

타입 코드를 상태 / 전략 패턴으로 전환270p

Page 34: Refactoring with Ruby (리펙토링 루비)

동기• 조건문 제거• 타입 코드의 자유로운 동적 전환

Page 35: Refactoring with Ruby (리펙토링 루비)

1. 타입 코드 필드 자체 캡슐화• 모듈 확장과 동일

Page 36: Refactoring with Ruby (리펙토링 루비)

2. 타입에 해당하는 클래스 작성• Class RigidMountainBike

end

Class FrontSuspensionMountainBikeend

Class FullSuspensionMountainBikeend

Page 37: Refactoring with Ruby (리펙토링 루비)

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 (리펙토링 루비)

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 (리펙토링 루비)

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 (리펙토링 루비)

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 (리펙토링 루비)

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 (리펙토링 루비)

하위클래스를 필드로 전환283p

Page 43: Refactoring with Ruby (리펙토링 루비)

동기• 하위 클래스가 상수 메서드만 정의–상속 구조가 복잡도를 증가시킴

Page 44: Refactoring with Ruby (리펙토링 루비)

리펙토링 전• 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 (리펙토링 루비)

1. 생성자를 팩토리 메서드로 전환• Class Person

def self.create_femaleFemale.new

end

def self.create_maleMale.new

endend

Page 46: Refactoring with Ruby (리펙토링 루비)

2. 호출 코드를 팩토리 메서드로 전환• Scott = Male.new• -> Scott = Person.create_male

Page 47: Refactoring with Ruby (리펙토링 루비)

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 (리펙토링 루비)

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 (리펙토링 루비)

속성 초기화를 사용시로 미루기287p

Page 50: Refactoring with Ruby (리펙토링 루비)

동기• 가독성–초기화 메서드가 복잡한 경우 초기화 로직을 분리

Page 51: Refactoring with Ruby (리펙토링 루비)

방법 1. ||= 사용하기 (before)• Class Employee

attr_reader :emails, :voice_mails

def initialize@emails = []@voice_mails = []

endend

Page 52: Refactoring with Ruby (리펙토링 루비)

방법 1. ||= 사용하기 (after)• Class Employee

def emails@emails ||= []

end

def voice_mails@voice_mails ||= []

endend

Page 53: Refactoring with Ruby (리펙토링 루비)

방법 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 (리펙토링 루비)

방법 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 (리펙토링 루비)

속성 초기화를 생성 시로 당기기290p

Page 56: Refactoring with Ruby (리펙토링 루비)

동기• 가독성–어떤 사람은 한 곳에 있는게 편하다 !;;

Page 57: Refactoring with Ruby (리펙토링 루비)

방법• 다시 원래 대로…

• 두 기법 중 하나를 선택하여 일관되게 사용하는 것이 중요

Page 58: Refactoring with Ruby (리펙토링 루비)

감사합니다