The document discusses 24 dependency breaking techniques for testing legacy code. Some key techniques include: adapt parameter to break dependencies on parameter classes; break out method object to separate logic into new classes; extract and override methods, factories, and getters to inject test logic; introduce instance delegators and static setters to test static methods; and subclass and override methods to break dependencies in object-oriented languages. The techniques aim to separate dependencies but may reduce readability and introduce errors if misused. Legacy code refactoring is challenging, so imperfect techniques are better than no alternatives.
15. 7. Extract and Override Factory Method
Class Class Class for test
constructor( ) { constructor( ) {
… …
object = new A() object = call A();
… …
} }
@Override
Factory method A(){ Factory method A(){
return new A() …
} }
16. When to use?
- trying to test code that creates objects in constructor.
17. 8. Extract and Override Getter
Class Class Class for test
constructor( ) { constructor( ) {
… …
object = New A() object = null;
… …
} }
@Override
object
Getter Method getA(){ Getter Method getA(){
if A is null …
object return new A(); }
return A;
}
getA()
getA()
Lazy initialize
18. When to use?
- trying to test code that creates objects in constructor
- language doesn’t support virtual function call in a derived class
from base class’s constructor
22. When to use?
- trying to test code that break dependency
23. 11. Introduce Instance Delegator
Class Class Class for test
Static Method A( ) { Static Method A( ) {
… …
} }
@Override
Method A’(){ Method A’(){
Class.A()
A(); …
} }
Class.A()
instance.A’()
instance.A’()
24. When to use?
- static method has logic that hard to test
- it’s weird
- 더 큰 리팩토링의 시작
25. 12. Introduce Static Setter
Class Class Class for test
Static instance Static instance
private constructor( ) { protected constructor( ) {
} }
Static Method getInst(){ Static Method getInst(){
if instance is null if instance is null
instance = new Class(); instance = new Class();
return instance; return instance;
} }
@Override
Method setInstance(…) { Method setInstance(…) {
instance = … test logic
}
}
26. When to use?
- Singleton test
- Protected constructor = can subclassing = 구리다
- singleton을 코드로 강제하는 것보다는 팀원들이
모두 이해하는게 더 중요하다?
27. 13. Link Substitution
Global reference for test
Call history
Value history
Method A() {
…
}
Method A() {
record call history
record value history
…
}
28. When to use?
- to break dependencies in procedural language like C.
29. 14. Parameterize Constructor
Class Class Class for test
constructor( ) { constructor( ) {
… this( new A() );
object = new A(); }
…
}
constructor( A ) { constructor( A ) {
… …
object = A; object = test;
… …
} }
30. When to use?
- to separate the object that created in constructor.
has dependencies on parameter’s class.
but it’s a small concern.
31. 15. Parameterize Method
Class Class
Method( ) { Method( A ) {
… …
object = new A(); object = A;
… …
} }
32. When to use?
-to separate the object that created in method.
dependency issue?
Extract and Override Factory Method is alternative way.
40. When to use?
- to break dependencies in procedural language like C.
Different points of view
- horribly unsafe VS useful tool
41. 20. Replace Global Reference with Getter
Class Class Class for test
Global references A Global references A
A getA
A getA
@Override
Getter Method getA() { Getter Method getA() {
return A; …
} }
42. When to use?
- separate dependencies on global references
43. 21. Subclass and Override Method
Class Class
private Method A(){ protected Method A(){
… …
} }
subClass for test
@Override
protected Method A(){
…
}
44. When to use?
- break dependencies in object-oriented language.
Many of the other dependency-breaking techniques
are variations on it.
45. 22. Supersede Instance Variable
Class Class Class for test
variable variable
constructor( ) { constructor( ) {
variable; variable;
} }
@Override
supersedeVariable(A) { supersedeVariable(A) {
variable = A; …
} }
46. When to use?
- separate objects that created in constructor.
- language disallows overrides of virtual function calls
in constructors.
Use uncommon prefix : supersede
개발자들이 잘못 사용할지도 모르니
잘 모르는 uncommon 한 단어를 prefix 로 사용하자??
47. 23. Template Redefinition
generics
Method ( A ){
...
}
Concrete type Concrete type
for production for test
48. When to use?
- language supplies generics and a way of aliasing types.
49. 24. Text Redefinition
TestClass
Class Class
def Method A()
def Method A() end
…
logic
…
end
TEST( ) {
…
test Class
}
50. When to use?
- break dependencies in interpreted language like Ruby.
51. 전체 정리하면서 느낀 점
- 억지스러운 면이 자주 보인다.
- dependency 는 깨질지 몰라도 Readability는 더 낮아진다.
- 코드가 잘못 사용될 수 있으니 개발자가 더 주의 깊게 살펴봐야 한다.
- 아무래도 LegacyCode 와의 싸움이 힘겹긴 한가보다. 이해하자.
- 억지스럽고 지저분한 방법일지라도 대안이 없다면
그게 바로 최선의 방법이다.
- 원문이 훨씬 보기 쉽다. (번역 X)
52. Reference
레거시코드 활용 전략
http://www.yes24.com/24/goods/3092523