370 likes | 615 Views
아 . 꿈 . 사 Annual Summary 2008. Refactoring To Patterns 이동현. Refactoring. Patterns. XP. 이 책은 . XP 에서 왜 패턴을 언급 하지 않는가 ? 마틴 파울러는 그의 저서에서 패턴을 고려한 리팩토링은 몇 가지 안 되었고 , 누군가 더 많은 패턴을 고려한 리팩토링을 써 주길 바랬다 . 좀 더 실용적인 예제가 필요했다 . Standing on the Shoulders of Giants. 일반적으로 . 패턴을 공부했다 .
E N D
아.꿈.사 Annual Summary 2008 Refactoring To Patterns이동현
Refactoring Patterns XP
이 책은.. • XP에서 왜 패턴을 언급 하지 않는가? • 마틴 파울러는 그의 저서에서 패턴을 고려한 리팩토링은 몇 가지 안 되었고, 누군가 더 많은 패턴을 고려한 리팩토링을 써 주길 바랬다. • 좀 더 실용적인 예제가 필요했다.
일반적으로.. • 패턴을 공부했다. • 패턴은 많은 유용한 디자인 아이디어 전달. • 몇 가지 패턴 습득 후. • 스스로 좋은 소프트웨어 디자이너로 생각되어 흡족하다. • 그러나.. 가끔 over-engineering 에 .. • 경험이 쌓여 스킬이 늘어 나의 스타일에 변화가 생겼다. • XP의 수용으로 과도한 up-front 디자인의 자제.
이 책을 쓴 이유 • Over-Engineering • Under-Engineering • The Pattern Panacea • Test Driven Development and Continuous Refactoring • Refactoring and Patterns • Evolutionary Design
Over-Engineering • 시스템의 미래의 요구사항을 알고 있다는 착각의 발현. • 예측이 틀리면? -> 모든 게 낭비. • 결과로 불필요한 복잡성의 증가와 부정적 side-effect. • 아주 조용하게 일어난다.
Under-Engineering • Poor Design • 시간이 없다.. 빨리 빨리.. 그래서.. • 좋은 디자인이 뭐야? 부족한 실력.. • 새로운 기능을 빨리 보고싶다. • 한번에 여러 프로젝트 참여. • 결국.. • Fast, slow, slower 리듬으로 귀결.
The Pattern Panacea • 닭 잡는데 소 잡는 칼 쓰기.. • 패턴 중독.. • 디자인 패턴은 만병통치약이 아니다. • 진정으로 더 나아가기 위해서는 패턴에 대한 맹신을 접어라.
TDD & Continuous Refactoring • Ask • Respond • Refind • Repeat
Refactoring and Patterns • 마틴의리팩토링을 적용하면서 디자인 개선에 패턴이 도움이 되는걸 발견했다. • 디자인 패턴만 공부하면 원래의 핵심인 동기에 대한 부분을 놓치기 쉽다. - Intent 에 집중하게 된다. • Applicability section 이라도 자세히 봤다면 .. Our design patterns capture many of the structures that result from refactoring... Design patterns thus provide targets for your refactoring. [DP, 354] There is a natural relation between patterns and refactorings. Patterns are where you want to be; refactorings are ways to get there from somewhere else. [F,107]
Evolutionary Design 조상으로부터 물려받은 것이 있거든 그것을 얻되 네 것이 되게 하라. - Goethe 결과 보다는 과정을 공부하는 것이 중요하다. 진짜 지혜는 그 과정 속에 숨어 있다. 패턴을 리팩터링과 별개로 생각 하지 말고, 리팩터링의 문맥에서 바라보라.
Refactoring • 새로운 코드를 더 쉽게 추가할 수 있도록 하기 위해. • 기존 코드의 설계를 개선하기 위해. • 기존 코드를 더 잘 이해하기 위해. • 덜 짜증나는 코드로 만들기 위해. • 많은 눈. • John Thompson 씨의 모자가게 • 깨진 창문. • 작은 단계. • 점심도 못 먹고 한 리팩터링. • 설계 부채. • 이걸로 관리자를 설득 하라.
Patterns • 패턴 목표 • 패턴 지향 • 패턴 제거
Code Smells • 설계 문제 • 중복된 코드 • 코드의 의미가 불명확 함 • 코드가 복잡하다. • 이런 것들은 구체적인 가이드 라인이 되기엔 너무 추상적이다. • 구체적인 기준 제시.
Catalog of Code Smells • Duplicated Code • Long Method • Conditional Complexity • Primitive Obsession • Indecent Exposure • Solution Sprawl • Alternative Classes with Different Interfaces • Lazy Class • Large Class • Switch Statements • Combinational Explosion • Oddball Solution
Alternative Classes with Different Interfaces • Unify Interfaces with Adapter( 247 ). • Combinatorial Explosion • Replace Implicit Language with Interpreter(269) • Conditional Complexity • Replace Conditional Logic with Strategy(129) • Move Embellishment to Decorator(144) • Replace State-Altering Conditionals with State(166) • Introduce Null Object(301) • Duplicated Code • Form Template Method(205) • Introduce Polymorphic Creation with Factory Method(88) • Chain Constructors(340) • Replace One/Many Distinctions with Composite(224) • Extract Composite(214) • Unify Interface with Adapter(247) • Introduce Null Object(301) • Indecent Exposure • Encapsulate Classes with Factory(80) • Large Class • Replace Conditional Dispatcher with Command(191) • Replace State-Altering Conditionals with State(166) • Replace Implicit Language with Interpreter(269) • Lazy Class • Inline Singleton(114) • Long Method • Compose Method(123) • Move Accumulation to Collection Parameter(313) • Replace Conditional Dispatcher with Command(191) • Move Accumulation to Visitor(320) • Replace Conditional Logic with Strategy(129) • Oddball Solution • Unify Interfaces with Adapter(247) • Primitive Obsession • Replace Type Code with Class(286) • Replace Sate-Altering Conditionals with State(166) • Replace Conditional Logic with Strategy(129) • Replace Implicit Tree with Composite(178) • Replace Implicit Language with Interpreter(269) • Move Embellishment to Decorator(144) • Encapsulate Composite with Builder(96) • Solution Sprawl • Move Creation Knowledge to Factory(68) • Switch Statements • Replace Conditional Dispatcher with Command(191) • Move Accumulation to Visitor(320)
리팩터링 형식 • States Pattern Name and Intent • Gives an application example • Discusses motivation • Benefits and Liabilities • Mechanics • Specific things to do • Presents detailed example
요약 • 어떤 클래스의 인스턴스를 생성할 때 그것이 제공하는 여러 생성자 중 어떤 것을 호출해야 할지 결정하기 어렵다. • 인스턴스를 생성해 리턴하는 생성 메서드로 각 생성자를 대체하여 그 용도가 명확하게 드러나도록 한다.
동기 • 생성자가 많으면 그 자체만 보고 용도를 파악하기 어렵다. • 생성자가 많을 수록, 프로그래머가 잘못 선택할 확률도 높아진다. • 어떤 생성자를 사용할지 고민하면 개발이 늦어진다. • 생성 메서드를 사용해서 이러한 문제점을 제거한다. • Extract Class , Extract Subclass 리팩터링을 먼저 고려.
장점과 단점 + 상태 전이 로직을 줄이거나 없앨 수 있다. + 복잡한 상태 전이 로직이 단순해 진다. + 상태 전이 로직을 더 쉽게 알아볼 수 있게 된다. - 원래의 상태 전이 로직이 이해하기에 별로 복잡하지 않다면, 괜히 디자인만 복잡하게 만드는 것이다.
절차 • ….
예제 public class Loan { public Load( double commitment, intriskRating, Date maturity) { this(commitment, 0.00, riskRating, maturity, null); } public Load( double commitment, intriskRating, Date maturity, Date expiry ) { this(commitment, 0.00, riskRating, maturity, expiry ); } public Load( double commitment, double outstanding, intriskRating, Date maturity, Date expiry) { this(null, commitment, outstanding, riskRating, maturity, expiry ); } public Load( CapitalStrategycapitalStrategy, double commitment, intriskRating, Date maturity, Date expiry) { this( capitalStrategy, commitment, 0.0, riskRating, maturity, expiry ); } public Load( CapitalStrategycapitalStrategy, double commitment, double outstanding, intriskRating,Datematurity, Date expiry ) { this.commitment = commitment; this.outstanding = outstanding; this.riskRating = riskRating; this.maturity = maturity; this.expiry = expiry; this.capitalStrategy = captialStrategy; if(capitalStrategy == null ) { if( expiry == null ) this.capitalStrategy = new CapitalStrategyTermLoan(); else if( maturity == null ) this.capitalStrategy = new CapitalStrategyRevolver(); else this.capitalStrategy = new CapitalStrategyRCTL(); } } }
1. Load 생성자를 하나 고르고 이 생성자를 사용하는 클라이언트 코드를 찾는다. public class CapitalCalculationTests... public void testTermLoanNoPayments() { ... Loan temLoadn = new Loan(commitment, riskRating, maturity); ... }
2. Extract Method 리팩터링을 적용해 CreateTermLoan이라는 public 메서드를 만든다. public class CapitalCalculationTests... public void testTermLoanNoPayments() { ... Loan temLoadn = new Loan(commitment, riskRating, maturity); ... } public class CapitalCalculationTests... public void testTermLoanNoPayments() { //... Loan temLoadn = createTermLoan(commitment, riskRating, maturity); //... } public Loan createTermLoan(double commitment, intriskRating, Date maturity) { return new Loan(commitment, riskRating, maturity); } Extract Method
3. Move Method 리팩터링을 적용해 생성 메서드createTermLoan()을 Loan 클래스로 옮긴다. public class CapitalCalculationTests... public void testTermLoanNoPayments() { //... Loan temLoadn = createTermLoan(commitment, riskRating, maturity); //... } public Loan createTermLoan(double commitment, intriskRating, Date maturity) { return new Loan(commitment, riskRating, maturity); } Move Method public class Loan public static Loan createTermLoan(double commitment, intriskRating, Date maturity) { return new Loan(commitment, riskRating, maturity); }
4. 선택한 생성자를 사용하는 모든 클라이언트 코드를 수정한다. public class CapitalCalculationTests... public void testTermLoanNoPayments(){ //... Loan temLoadn = Loan.createTermLoan(commitment, riskRating, maturity); //... } }
5. 이제 선택한 생성자를 사용하는 곳은 CreateTermLoan뿐이다.Inline method 를 적용해 생성자 호출 부분을 제거한다. public class Loan public Loan(double commitment, intriskRating, Date maturity){ this(commitment, 0.00, riskRating, maturity, null) } public static Loan createTermLoan(double commitment, intriskRating, Date maturity) { return new Loan(commitment, 0.00, riskRating, maturity, null); }
6. 다른 생성자에 대해서도 1-5 과정을 반복한다.
Replace Implicit Language with Interpreter • 한 클래스 내의 여러 메서드에서 일종의 묵시적 언어를 이루는 요소들을 조합하고 있다면. • 그 묵시적 언어의 요소들을 각각의 클래스로 정의하고 그 객체의 조합을 통해 해석 가능한 수식을 만들어 낼 수 있도록 한다.
묵시적 언어 • 10달러 이하의 상품을 찾아라. • 흰색이 아니고 10달러 이하인 상품을 찾아라. • 파란색이고 작으며 20달러 이하인 상품을 찾아라. ProductFinder… public List byColor(..); … public List belowPriceAvoidingAColor(float price, Color color); public List byColorAndBelowPrice(Color color, float price); public List byColorSizeAndBelowPrice(…)
Interpreter belowPriceAvoidingAColor… and AndSpec spec = new AndSpec( new BelowPriceSpec(price), new NotSpec( new ColorSpec(color) ) ); Price < target price not Color == target color