볞묞윌로 걎너뛰Ʞ

🌈 Chapter 9: 유연한 섀계

📚 개방-폐쇄 원칙​

  • 개방-폐쇠 원칙은 소프튞웚얎 개첎(큎래슀, 몚듈, 핚수 등등)는 확장에 대핮 ì—Žë € 있얎알 하고, 수정에 대핎서는 닫혀 있얎알 한닀.
  • 확장에 대핮 ì—Žë € 있닀: 애플늬쌀읎션의 요구사항읎 변겜될 때 읎 변겜에 맞게 새로욎 동작을 추가핎서 애플늬쌀읎션의 Ʞ능을 확장할 수 있닀.
  • 수정에 대핮 닫혀 있닀: Ʞ졎의 윔드륌 수정하지 않고도 애플늬쌀읎션의 동작을 추가하거나 변겜할 수 있닀.
  • 개방-폐쇄 원칙은 유연한 섀계란 윔드륌 수정하지 않고도 애플늬쌀읎션의 동작을 확장할 수 있는 섀곌고 읎알Ʞ한닀.

🎈 컎파음타임 의졎성을 고정시킀고 런타임 의졎성을 변겜하띌​

  • 런타임 의졎성은 싀행시에 협력에 찞여하는 객첎듀 사읎의 ꎀ계닀.
  • 컎파음타임 의졎성은 윔드에서 드러나는 큎래슀듀 사읎의 ꎀ계닀.
  • 개방-폐쇄 원칙을 수용하는 윔드는 컎파음타임 의졎성을 수정하지 않고도 런타임 의졎성을 쉜게 변겜할 수 있닀.
  • 의졎성 ꎀ점에서 개방-폐쇄 원칙을 따륎는 섀계란 컎파음타임 의졎성은 유지하멎서 런타임 의졎성의 가능성을 확장하고 수정할 수 있는 구조띌고 할 수 있닀.

🎈 추상화가 핵심읎닀​

  • 개방-폐쇄 원칙의 핵심은 추상화에 의졎하는 것읎닀.
  • 추상화 곌정을 거치멎 묞맥읎 바뀌더띌도 변하지 않는 부분만 낚게 되고 묞맥에 따띌 변하는 부분은 생략된닀.
  • 개방-폐쇄 원칙의 ꎀ점에서 생략되지 않고 낚겚지는 부분은 닀양한 상황에서의 공통점을 반영한 추상화의 결곌묌읎닀. 공통적읞 부분은 묞맥읎 바뀌더띌도 변하지 ì•Šì•„ì•Œ 한닀. 공통적읞 부분은 묞맥읎 바뀌더띌도 변하지 ì•Šì•„ì•Œ 한닀. 닀시 말핎서 수정할 필요가 없얎알 한닀. 따띌서 추상화 부분은 수정에 닫혀 있닀.
  • 추상화륌 통핎 생략된 부분은 확장의 여지륌 낚ꞎ닀. 얞제띌도 추상화의 생략된 부분을 채워넣음윌로썚 새로욎 묞맥에 맞게 Ʞ능을 확장할 수 있닀. 따띌서 추상화는 섀계의 확장을 가능하게 한닀.
  • 개방-폐쇄 원칙에서 폐쇄륌 가능하게 하는 것은 의졎성의 방향읎닀. 수정에 대한 영향을 최소화하Ʞ 위핎서는 몚든 요소가 추상화에 의졎핎알 한닀. (Movie는 할읞 정책을 추상화한 DiscountPolicy에 대핎서 의졎)
  • 죌의할 점은 추상화륌 했닀고 í•Žì„œ 몚든 수정에 대핮 섀계가 폐쇄되는 것은 아니닀. 변겜에 의한 파꞉횚가륌 최대한 플하Ʞ 위핎서는 변하는 것곌 변하지 않는 것읎 묎엇읞지륌 읎핎하고 읎륌 추상화의 목적윌로 삌아알만 한닀.

📚 생성 사용 분늬​

  • 결합도가 높아질수록 개방-폐쇄 원칙을 따륎는 구조륌 섀계하Ʞ가 얎렀워진닀. 알아알 하는 지식읎 많윌멎 결합도가 높아진닀.
  • 객첎의 타입곌 생성자에 전달핎알 하는 읞자에 대한 곌도한 지식은 윔드륌 특정한 컚텍슀튞에 강하게 결합시킚닀.
  • 동음한 큎래슀 안에서 객첎 생성곌 사용읎띌는 두 가지 읎질적읞 목적을 가진 윔드가 공졎하는 것읎 묞제읎닀.
  • 유연하고 재사용 가능한 섀계륌 원한닀멎 객첎와 ꎀ렚된 두 가지 책임을 서로 닀륞 객첎로 분늬핎알 한닀. 하나는 객첎륌 생성하는 것읎고, 닀륞 하나는 객첎륌 사용하는 것읎닀.
  • 한 마디로 말핎서 객첎에 대한 생성곌 사용을 분늬핎알 한닀.
  • 사용윌로부터 생성을 분늬하는 데 사용되는 가장 볎펞적읞 방법은 객첎륌 생성할 책임을 큎띌읎얞튞로 옮Ʞ는 것읎닀.
public class Client {
public Money getAvatarFee() {
Movie avatar = new Movie("아바타",
Duration.ofMinutes(120),
Money.wons(10000),
new AmountDiscountPolicy(...)
);

return avatar.getFee();
}
}

🎈 FACTORY 추가하Ʞ​

  • 위 윔드에서 Movie의 생성 책임을 Client로 옮게닀. 만앜 Client도 특정한 컚텍슀튞에 묶읎지 않Ʞ륌 바란닀고 가정하멎 Client도 생성하는 동시에 getFee 메시지도 핚께 전송하고 있닀. 슉, 생성곌 사용의 책임을 핚께 지니고 있닀.
  • 읎 묞제는 Movie의 묞제륌 핎결했던 방법곌 동음한 방법윌로 í•Žê²°í•  수 있닀.
  • 읎 겜우 객첎 생성곌 ꎀ렚된 책임만 전닎하는 별도의 객첎륌 추가하고 Client는 읎 객첎륌 사용하도록 만듀 수 있는데 읎 처럌 생성곌 사용을 분늬하Ʞ 위핎 객첎 생성에 특화된 객첎륌 FACTORY띌고 부륞닀.
public class Factory {
public Movie createAvatarMovie() {
return new Movie("아바타",
Duration.ofMinutes(120),
Money.wons(1000),
new AmountDiscountPolicy(...)
);
}
}
  • 읎제 Client는 Factory륌 사용핎서 생성된 Movie의 읞슀턎슀륌 반환받아 사용하Ʞ만 하멎 된닀.
public class Client {
private Factory factory;

public Client(FACTORY factory) {
this.factory = factory;
}

public Money getAvatarFee() {
Movie avatar = factory.createAvatarMovie();

return avatar.getFee();
}
}

🎈 순수한 가공묌에게 책임 할당하Ʞ​

  • 책임 할당의 가장 Ʞ볞읎 되는 원칙은 책임을 수행하는 데 필요한 정볎륌 가장 많읎 알고 있는 INFORMATION EXPERT에게 책임을 할당하는 것읎닀. 도메읞 몚덞은 INFORMATION EXPERT륌 ì°Ÿêž° 위핎 ì°žì¡°í•  수 있는 음찚적읞 재료닀. ì–Žë–€ 책임을 할당하고 싶닀멎 제음 뚌저 도메읞 몚덞 안의 개념 쀑에서 적절한 후볎가 졎재하는지 찟아뎐알 한닀.
  • FACTORY는 도메읞 몚덞에 속하지 않는닀. FACTORY륌 추가한 읎유는 순수하게 Ʞ술적읞 결정읎닀. 전첎적읞 결합도륌 낮추고 재사용성을 높읎Ʞ 위핎 도메읞 개념에게 할당돌 있던 객첎 생성 책임을 도메읞 개념곌는 아묎런 상ꎀㅇ니 없는 가공의 객첎로 읎동시킚 것읎닀.
  • 시슀템을 객첎로 분핎하는 데 크게 두 가지 방식읎 졎재하는데 하나는 표현적 분핎읎고 닀륞 하나는 행위적 분핎닀.
  • 표현적 분핎는 도메읞에 졎재하는 사묌 또는 개념을 표현하는 객첎듀을 읎용핎 시슀템을 분핎하는 것읎닀. 표현적 분핎는 도메읞 몚덞에 닎겚 있는 개념곌 ꎀ계륌 따륎며 도메읞곌 소프튞웚얎 사읎의 표현적 찚읎륌 최소화하는 것을 목적윌로 한닀.
  • 귞러나 종종 도메읞 개념을 표현하는 객첎에게 책임을 할당하는 것만윌로 부족한 겜우가 발생한닀. 몚든 책임을 도메읞 객첎에 할당하멎 낮은 응집도, 높은 결합도, 재사용성 저하와 같은 심각한 묞제점에 뎉착하게 될 가능성읎 높아진닀. 읎 겜우 도메읞 개념을 표현한 객첎가 아닌 섀계자가 펞의륌 위핎 임윌로 만듀얎낞 가공의 객첎에게 책임을 할당핎서 묞제륌 í•Žê²°í•Žì•Œ 하는데 읎처럌 책임을 할당하Ʞ 위핎 찜조되는 도메읞곌 묎ꎀ한 읞공적읞 객첎륌 PURE FABRICATION(순수한 가공묌)읎띌고 부륞닀.
  • ì–Žë–€ 행동을 추가하렀고 하는데 읎 행동을 책임질 마땅한 도메읞 개념읎 졎재하지 않는닀멎 PURE FABRICATION을 추가하고 읎 객첎에게 책임을 할당핎알 한닀. ê·ž 결곌로 볎통 특정한 행동을 표현하는 것읎 음반적읎닀. 따띌서 PURE FABRICATION은 표현적 분핎볎닀 행위적 분핎에 의핎 생성된닀.
  • 뚌저 도메읞의 볞질적읞 개념을 표현하는 추상화륌 읎용핎 애플늬쌀읎션을 구축하고 만앜 도메읞 개념읎 만족슀럜지 못하닀멎 읞공적읞 객첎륌 찜조한닀.
  • FACTORY는 객첎의 생성 책임을 할당할만한 도메읞 객첎가 졎재하지 않을 때 선택할 수 있는 PURE FABRICATION을 포핚한닀.

📚 의졎성 죌입​

  • 사용하는 객첎가 아닌 왞부의 독늜적읞 객첎가 읞슀턎슀륌 생성한 후 읎륌 전달핎서 의졎성을 핎결하는 방법을 의졎성 죌입읎띌고 부륞닀.
  • 읎 Ʞ법은 왞부에서 의졎성의 대상을 í•Žê²°í•œ 후 읎륌 사용하는 객첎 쪜에 죌입한닀.
  • 의졎성 죌입은 의졎성 í•Žê²° 방법곌 ꎀ렚읎 깊닀. (생성자 죌입, setter 죌입, 메서드 죌입)
  • 닀음 예제는 Movie의 생성자륌 읎용핎 의졎성을 죌입하Ʞ 때묞에 생성자 죌입읎띌고 부륞닀.
Movie avatar = new Movie("아바타",
Duration.ofMinutes(120),
Money.wons(10000),
new AmountDiscountPolicy(...)
);
  • setter 죌입은 읎믞 생성된 Movie에 대핮 setter 메서드륌 읎용핎 의졎성을 핎결한닀. setter 죌입의 장점은 의졎성 대상을 런타임에 변겜할 수 있닀. 얞제띌도 의졎 대상을 교첎할 수 있닀.
  • 닚점은 객첎가 올바륎게 생성되Ʞ 위핎 ì–Žë–€ 의졎성읎 필수적읞지륌 명시적윌로 표현할 수 없닀.
avatar.setDiscountPolicy(new AmountDiscountPolicy(...));
  • 메서드 죌입은 메서드 혞출 죌입읎띌고도 부륎며 메서드가 의졎성을 필요로 하는 유음한 겜우음 때 사용할 수 있닀.
avatar.calculateDiscountAmount(screening, new AmountDiscountPolicy(...));

🎈 숚겚진 의졎성은 나쁘닀​

  • 의졎성 죌입 왞에 의졎성을 í•Žê²°í•  수 있는 방법 쀑 하나읞 SERVICE LOCATOR 팚턎읎 있닀.
  • 읎 팚턎은 의졎성을 í•Žê²°í•  객첎듀의 볎ꎀ하는 음종의 저장소닀. 왞부에서 객첎에게 의졎성을 전달하는 의졎성 죌입곌 달늬 SERVICE LOCATOR의 겜우 객첎가 직접 SERVICE LOCATOR에게 의졎성을 핎결핎쀄 것을 요청한닀.
  • 예륻 듀얎 ServiceLocator띌는 큎래슀가 SERVICE LOCATOR의 역할을 수행하고 Movie는 직접 ServiceLocator의 메서드륌 혞출핎서 DiscountPolicy에 대한 의졎성을 핎결한닀.
public class Movie {
// ...
private DiscountPolicy discountPolicy;

public Movie(String title, Duration runningTime, Money fee) {
this.title = title;
this.runningTime = runningTime;
this.fee = fee;
this.discountPolicy = ServiceLocator.discountPolicy();
}
}
  • ServiceLocator는 DiscountPolicy의 읞슀턎슀륌 등록하고 반환할 수 있는 메서드륌 구현한 저장소닀.
public class ServiceLocator {
private static ServiceLocator soleInstance = new ServiceLocator();
private DiscountPolicy discountPolicy;

public static DiscountPolicy discountPolicy() { // 읞슀턎슀 반환
return soleInstance.discountPolicy;
}

public static void provide(DiscountPolicy discountPolicy) { // 읞슀턎슀 등록
soleInstance.discountPolicy = discountPolicy;
}

private ServiceLocator() {}
}
  • 닀음곌 같읎 읞슀턎슀륌 ServiceLocator에 읞슀턎슀륌 등록한 후 Movie륌 생성하멎 된닀.
ServiceLocator.provide(new AmountDiscountPolicy(...));
Movie avatar = new Movie("아바타",
Duration.ofMinutes(120),
Money.wons(10000)
);

ServiceLocator.provide(new PercentDiscountPolicy(...));
Movie avatar = new Movie("아바타",
Duration.ofMinutes(120),
Money.wons(10000)
);
  • SERVICE LOCATOR 팚턎의 가장 큰 닚점은 의졎성을 감춘닀. Movie는 DiscountPolicy에 의졎하고 있지만 Movie의 퍌랔늭 읞터페읎슀 얎디에도 읎 의졎성에 대한 정볎가 표시돌 있지 ì•Šë‹€.
  • 의졎성을 구현 낎부로 감출 겜우 의졎성곌 ꎀ렚된 묞제가 컎파음타임읎 아닌 런타임에 가서알 발견된닀는 사싀을 알 수 있닀. 숚겚진 의졎성읎 읎핎하Ʞ 얎렵고 디버깅하Ʞ 얎렀욎 읎유는 묞제저을 발견할 수 있는 시점을 윔드 작성 시점읎 아니띌 싀행 시점윌로 믞룚Ʞ 때묞읎닀. 의졎성을 숚Ʞ는 윔드는 닚위 테슀튞 작성도 얎렵닀.
  • 숚겚진 의졎성읎 가지는 가장 큰 묞제점은 의졎성을 읎핎하Ʞ 위핎 윔드의 낎부 구현을 읎핎할 것을 강요한닀. 따띌서 숚겚진 의졎성은 캡슐화륌 위반한닀. 결곌적윌로 의졎성을 구현 낎부로 감추도록 강요하는 SERVICE LOCATOR는 캡슐화륌 위반할 수밖에 없닀.
  • 명시적읞 의졎성읎 숚겚진 의졎성볎닀 좋닀. 가꞉적 의졎성을 객첎의 퍌랔늭 읞터페읎슀에 녞출핎알 한닀.
  • 가능하닀멎 의졎성윌 명시적윌로 표현할 수 있는 Ʞ법을 사용하띌.

📚 의졎성 역전 원칙​

🎈 추상화와 의졎성 역전​

  • 객첎 사읎의 협력읎 졎재할 때 ê·ž 협력의 볞질을 ë‹Žê³  있는 것은 상위 수쀀의 정책읎닀. (할읞 ꞈ액을 계산할 것읞지가 아닌 영화륌 계산하는 것) 닀시 말핎서 ì–Žë–€ 협력에서 쀑요한 정책읎나 의사결정, 비슈니슀의 볞질을 ë‹Žê³  있는 것은 상위 수쀀의 큎래슀닀.
  • 상위 수쀀의 큎래슀는 ì–Žë–€ 식윌로든 하위 수쀀의 큎래슀에 의졎핎서는 안 된닀. (Movie는 AmountDiscountPolicy에 의졎핎서는 안 된닀.)
  • 상위 수쀀의 큎래슀가 하위 수쀀의 큎래슀에 의졎하멎 상위 수쀀의 큎래슀륌 재사용할 때 하위 수쀀의 큎래슀도 필요하Ʞ 때묞에 재사용하Ʞ가 얎렀워진닀.
  • 하위 수쀀의 변겜윌로 읞핎 상위 수쀀읎 변겜돌서는 안 된닀.
  • 읎 겜우에도 í•Žê²°í•  방법은 추상화닀. (Movie와 AmountDiscountPolicy 사읎에 추상 큎래슀읞 DiscountPolicy) 상위 수쀀의 큎래슀와 하위 수쀀의 큎래슀 몚두 추상화에 의졎한닀.
  • 유연하고 재사용 가능한 섀계륌 원한닀멎 몚두 의졎성의 방향읎 추상 큎래슀나 읞터페읎슀와 같은 추상화륌 따띌알 한닀. 구첎 큎래슀는 의졎성의 시작점읎얎알 한닀.
  • 의졎성 역전 원칙읎란 상위 수쀀의 몚듈은 하위 수쀀의 몚듈에 의졎핎서 안되고, 추상화는 구첎적읞 사항에 의졎핎서는 안 된닀는 것읎닀. 슉, 상위와 하위 수쀀의 몚듈 둘 ë‹€ 추상화에 의졎핎알 하고, 구첎적읞 사항은 추상화에 의졎핎알 한닀.

🎈 의졎성 역전 원칙곌 팚킀지​

  • 역전은 의졎성의 방향뿐만 아니띌 읞터페읎슀의 소유권에도 적용된닀.
  • Movie의 재사용을 위핎 필요한 것읎 DiscountPolicy뿐읎띌멎 DiscountPolicy륌 Movie와 같은 팚킀지로 몚윌로 AmountDiscountPolicy와 PercentDiscountPolicy륌 별도의 팚킀지에 위치시쌜 의졎성 묞제륌 í•Žê²°í•  수 있닀. 따띌서 추상화륌 별도의 독늜적읞 팚킀지가 아니띌 큎띌읎얞튞가 속한 팚킀지에 포핚시쌜알 한닀.
  • 귞늬고 핚께 재사용될 필요가 없는 큎래슀듀은 별도의 독늜적읞 팚킀지에 몚아알 한닀. 마틮 파욞러는 읎 Ʞ법을 가늬쌜 SEPARATED INTERFACE 팚턎읎띌고 부륞닀.
  • 따띌서 의졎성 역정 원칙에 따띌 상위 수쀀의 협력 흐늄을 재사용하Ʞ 위핎서는 추상화가 제공하는 읞퍌테읎슀의 소유권 역시 역전시쌜알 한닀.
  • 객첎지향 팚러닀임에서 읞터페읎슀가 상위 수쀀 몚듈에 속한닀.

📚 유연성에 대한 조얞​

🎈 유연한 섀계는 유연성읎 필요할 때만 옳닀​

  • 유연하고 재사용 가능한 섀계란 런타임 의졎성곌 컎파음타임 의졎성의 찚읎륌 읞식하고 동음한 컎파음타임 의졎성윌로부터 닀양한 런타임 의졎성을 만듀 수 있는 윔드 구조륌 가지는 섀계륌 의믞한닀.
  • 하지만 유연하고 재사용 가능한 섀계가 항상 좋은 것은 아니닀. 닚순하고 명확한 섀계륌 가진 윔드는 읜Ʞ 쉜고 읎핎하Ʞ도 펞하닀. 변겜하Ʞ 쉜고 확장하Ʞ 쉬욎 구조륌 만듀Ʞ 위핎서는 닚순핚곌 명확핚의 믞덕을 버늬게될 가능성읎 높닀.
  • 유연성은 항상 복잡성을 수반한닀. 유연하지 않은 섀계는 닚순하고 명확하닀.
  • 섀계가 유연할수록 큎래슀 구조와 객첎 구조 사읎의 거늬는 점점 멀얎진닀. 유연한 섀계륌 닚순하고 명확하게 만드는 유음한 방법은 사람듀 간의 ꞎ밀한 컀뮀니쌀읎션뿐읎닀.
  • 불필요한 유연성은 불필요한 복잡성을 낳는닀. 닚순하고 명확한 핎법읎 귞런대로 만족슀럜닀멎 유연성을 제거핎알 한닀.

🎈 햡력곌 책임읎 쀑요하닀​

  • 섀계륌 유연하게 만듀Ʞ 위핎서는 협력에 찞여하는 객첎가 닀륞 객첎에게 ì–Žë–€ 메시지륌 전송하는지가 쀑요하닀.
  • 섀계륌 유연하게 믌듀Ʞ 위핎서는 뚌저 역할, 책임, 협력에 쎈점을 맞춰알 한닀.
  • 쀑요한 비슈니슀 로직을 처늬하Ʞ 위핎 책임을 할당하고 협력의 균형을 맞추는 것읎 객첎 생성에 ꎀ한 책임을 할당하는 것볎닀 우선읎닀.
  • 핵심은 객첎륌 생성하는 방법에 대한 결정은 몚든 책임읎 자늬륌 잡은 후 가장 마지막 시점에 낎늬는 것읎 적절하닀는 것읎닀.
  • 의졎성을 ꎀ늬핎햐 하는 읎유는 역할, 책임, 협력의 ꎀ점에서 섀계가 유연하고 재사용 가능핎알 하Ʞ 때묞읎닀. 따띌서 역할, 책임, 협력에 뚌저 집쀑핎알 한닀.