{"componentChunkName":"component---src-containers-post-index-tsx","path":"/backend/object/chapter10/상속과_코드_재사용/","result":{"pageContext":{"next":{"id":"b14e7f9b-76a7-59bf-b641-5c48cac92a69","html":"<h2 id=\"-91-개방-폐쇄-원칙\" style=\"position:relative;\"><a href=\"#-91-%EA%B0%9C%EB%B0%A9-%ED%8F%90%EC%87%84-%EC%9B%90%EC%B9%99\" aria-label=\" 91 개방 폐쇄 원칙 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>📖 9.1 개방-폐쇄 원칙</h2>\n<blockquote>\n<p>소프트웨어 개체(클래스, 모듈, 함수 등등)는 확장에 대해 열려 있어야 하고, 수정에 대해서는 닫혀 있어야 한다.</p>\n</blockquote>\n<ul>\n<li>확장에 대해 열려 있다: 애플리케이션의 요구사항이 변경될 때 이 변경에 맞게 새로운 '동작'을 추가해서 애플리케이션의 기능을 확장할 수 있다.</li>\n<li>수정에 대해 닫혀 있다: 기존의 '코드'를 수정하지 않고도 애플리케이션의 동작을 추가하거나 변경할 수 있다.</li>\n</ul>\n<h3 id=\"-911-컴파일타임-의존성을-고정시키고-런타임-의존성을-변경하라\" style=\"position:relative;\"><a href=\"#-911-%EC%BB%B4%ED%8C%8C%EC%9D%BC%ED%83%80%EC%9E%84-%EC%9D%98%EC%A1%B4%EC%84%B1%EC%9D%84-%EA%B3%A0%EC%A0%95%EC%8B%9C%ED%82%A4%EA%B3%A0-%EB%9F%B0%ED%83%80%EC%9E%84-%EC%9D%98%EC%A1%B4%EC%84%B1%EC%9D%84-%EB%B3%80%EA%B2%BD%ED%95%98%EB%9D%BC\" aria-label=\" 911 컴파일타임 의존성을 고정시키고 런타임 의존성을 변경하라 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 9.1.1 컴파일타임 의존성을 고정시키고 런타임 의존성을 변경하라</h3>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1114px; \"\n    >\n      <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 29.333333333333332%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAGCAIAAABM9SnKAAAACXBIWXMAABYlAAAWJQFJUiTwAAAA9klEQVQY013Ny27CMBCFYd7/7WikIEsE0hhiHNnO+JL4MjhTEWgX/Vaz+c8ctm1DxFIKIj6fz5xL/rVtGxHlnGNMMcZSyn7HdV0RkYgOiAgA4zgaY7TW0zRxfrter0qpUgoRzfMshJBSWmuVUm3bNk0DAK+YiEIIAOCcq7WGEB6PB+fcWltrJaIYY9jVWkvBJSwhhJzzJ35LKUkpT6eTlFJrzRjrus45573XWhtj1nX13l8ul/P5vCzL/5gxdjx+DcPrLed8GAbnHAAIIcZxFELc77fvnff+E9sUjbXTNHVdxxjr+14pBQDGmJTSe7rWaq19N39+AAmIU6iuKMtnAAAAAElFTkSuQmCC'); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"9.1\"\n        title=\"9.1\"\n        src=\"/static/c48c54664a1af80484ca8ea31a6937f8/cd536/9.1.png\"\n        srcset=\"/static/c48c54664a1af80484ca8ea31a6937f8/5a46d/9.1.png 300w,\n/static/c48c54664a1af80484ca8ea31a6937f8/0a47e/9.1.png 600w,\n/static/c48c54664a1af80484ca8ea31a6937f8/cd536/9.1.png 1114w\"\n        sizes=\"(max-width: 1114px) 100vw, 1114px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n        decoding=\"async\"\n      />\n    </span></p>\n<ul>\n<li>기존 클래스는 전혀 수정하지 않은 채 애플리케이션의 동작을 확장했다.</li>\n<li>단순히 새로운 클래스를 추가하는 것만으로 <code>Movie</code>를 새로운 컨텍스트에 사용되도록 확장할 수 있었던 것</li>\n</ul>\n<p>OCP를 수용하는 코드는 컴파일타임 의존성을 수정하지 않고도 런타임 의존성을 쉽게 변경할 수 있다. 의존성 관점에서 OCP를 따르는 설계란 컴파일타임 의존성은 유지하면서 런타임 의존성의 가능성을 확장하고 수정할 수 있는 구조라고 할 수 있다.</p>\n<h3 id=\"-912-추상화가-핵심이다\" style=\"position:relative;\"><a href=\"#-912-%EC%B6%94%EC%83%81%ED%99%94%EA%B0%80-%ED%95%B5%EC%8B%AC%EC%9D%B4%EB%8B%A4\" aria-label=\" 912 추상화가 핵심이다 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 9.1.2 추상화가 핵심이다</h3>\n<p>OCP의 핵심은 <strong>추상화에 의존하는 것</strong></p>\n<ul>\n<li>추상화란 핵심적인 부분만 남기고 불필요한 부분은 생략함으로써 복잡성을 극복하는 기법</li>\n<li>OCP의 관점에서 생략되지 않고 남겨지는 부분은 다양한 상황에서의 공통점을 반영한 추상화의 결과물</li>\n</ul>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">public abstract class DiscountPolicy {\n    private final List&lt;DiscountCondition&gt; conditions;\n    \n    protected DiscountPolicy(DiscountCondition... conditions) {\n        this.conditions = Arrays.asList(conditions);\n    }\n    \n    public Money calculateDiscountAmount(Screening screening) {\n        for (DiscountCondition condition : conditions) {\n            if (condition.isSatisfiedBy(screening)) {\n                return getDiscountAmount(screening);\n            }\n        }\n        return screening.getMovieFee();\n    }\n    protected abstract Money getDiscountAmount(Screening screening);\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>언제라도 추상화의 생략된 부분을 채워넣음으로써 새로운 문맥에 맞게 기능을 확장할 수 있다.</li>\n<li>OCP에서 폐쇄를 가능하게 하는 것은 의존성의 방향이다.</li>\n<li>수정에 대한 영향을 최소화하기 위해서는 모든 요소가 추상화에 의존해야 한다.</li>\n</ul>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">@AllArgsConstructor\npublic class Movie {\n\n    private String title;\n\n    private Duration runningTime;\n\n    @Getter\n    private Money fee;\n\n    private DiscountPolicy discountPolicy;\n    \n    public Money calculateMovieFee(Screening screening) {\n        return fee.minus(discountPolicy.calculateDiscountAmount(screening));\n    }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li><code>Movie</code>는 안정된 추상화인 <code>DiscountPolicy</code>에 의존하기 때문에 할인 정책을 추가하기 위해 <code>DiscountPolicy</code>의 자식 클래스를 추가하더라도 영향을 받지 않는다. 따라서 수정에 대해 닫혀 있다.</li>\n</ul>\n<p>추상화를 했다고 해서 모든 수정에 대해 설계가 폐쇄되는 것은 아니다. 추상화가 수정에 대해 닫혀 있을 수 있는 이유는 변경되지 않을 부분을 신중하게 결정하고 올바른 추상화를 주의 깊게 선택했기 때문이다.</p>\n<h2 id=\"-92-생성-사용-분리\" style=\"position:relative;\"><a href=\"#-92-%EC%83%9D%EC%84%B1-%EC%82%AC%EC%9A%A9-%EB%B6%84%EB%A6%AC\" aria-label=\" 92 생성 사용 분리 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>📖 9.2 생성 사용 분리</h2>\n<p>결합도가 높아질수록 개방-폐쇄 원칙을 따르는 구조를 설계하기가 어려워진다.</p>\n<ul>\n<li>알아야 하는 지식이 많으면 결합도도 높아진다.</li>\n<li>객체 생성에 대한 지식은 과도한 결합도를 초래하는 경향이 있다.\n<ul>\n<li>부적절한 곳에서 객체를 생성한다는 것이 문제❗️</li>\n</ul>\n</li>\n</ul>\n<p>유연하고 재사용 가능한 설계를 원한다면 <strong>객체에 대한 생성과 사용을 분리(seperating use from creation)</strong> 해야 한다.</p>\n<p>사용으로부터 생성을 분리하는 데 사용되는 가장 보편적인 방법은 <strong>객체를 생성할 책임을 클라이언트로 옮기는 것</strong></p>\n<h3 id=\"-921-factory-추가하기\" style=\"position:relative;\"><a href=\"#-921-factory-%EC%B6%94%EA%B0%80%ED%95%98%EA%B8%B0\" aria-label=\" 921 factory 추가하기 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 9.2.1 FACTORY 추가하기</h3>\n<blockquote>\n<p>생성과 사용을 분리하기 위해 객체 생성에 특화된 객체를 FACTORY라고 부른다.</p>\n</blockquote>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">public class Factory {\n    public Movie createAvatarMovie() {\n        return new Movie(&quot;아바타&quot;, Duration.ofMinutes(120), Money.wons(10000), new AmountDiscountPolicy(...));\n    }\n}</code>\n        </deckgo-highlight-code>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">@AllArgsConstructor\npublic class Client {\n    private Factory factory;\n\n    public Money getAvatarFee() {\n        Movie avatar = factory.createAvatarMovie();\n        return avatar.getFee();\n    }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>Client는 오직 사용과 관련된 책임만 지고 생성과 관련된 어떤 지식도 가지지 않을 수 있다.</li>\n</ul>\n<h3 id=\"-922-순수한-가공물에게-책임-할당하기\" style=\"position:relative;\"><a href=\"#-922-%EC%88%9C%EC%88%98%ED%95%9C-%EA%B0%80%EA%B3%B5%EB%AC%BC%EC%97%90%EA%B2%8C-%EC%B1%85%EC%9E%84-%ED%95%A0%EB%8B%B9%ED%95%98%EA%B8%B0\" aria-label=\" 922 순수한 가공물에게 책임 할당하기 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 9.2.2 순수한 가공물에게 책임 할당하기</h3>\n<p>도메인 모델은 Information expert를 찾기 위해 참조할 수 있는 일차적인 재료다.</p>\n<p>시스템을 객체로 분해하는 데는 크게 두 가지 방식이 존재</p>\n<ol>\n<li>\n<p>표현적 분해</p>\n<ul>\n<li>도메인에 존재하는 사물 또는 개념을 표현하는 객체들을 이용해 시스템을 분해하는 것</li>\n<li>도메인 모델에 담겨 있는 개념과 관계를 따르며 도메인과 소프트웨어 사잉의 표현적 차이를 최소화하는 것을 목적으로 함.</li>\n</ul>\n</li>\n<li>\n<p>행위적 분해</p>\n<ul>\n<li>순수한 가공물(pure fabrication): 책임을 할당하기 위해 창조되는 도메인과 무관한 인공적인 객체</li>\n</ul>\n</li>\n</ol>\n<p>도메인 개념이 만족스럽지 못하다면 주저하지 말고 인공적인 객체를 창조하라. 객체지향이 실세계의 모방이라는 말은 옳지 않다.</p>\n<h2 id=\"-93-의존성-주입\" style=\"position:relative;\"><a href=\"#-93-%EC%9D%98%EC%A1%B4%EC%84%B1-%EC%A3%BC%EC%9E%85\" aria-label=\" 93 의존성 주입 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>📖 9.3 의존성 주입</h2>\n<p>의존성 주입: 사용하는 객체가 아닌 외부의 독립적인 객체가 인스턴스를 생성한 후 이를 전달해서 의존성을 해결하는 방법</p>\n<ul>\n<li>생성자 주입: 객체를 생성하는 시점에 생성자를 통한 의존성 해결</li>\n<li>setter 주입: 객체 생성 후 setter 메서드를 통한 의존성 해결</li>\n<li>메서드 주입: 메서드 실행 시 인자를 이용한 의존성 해결</li>\n</ul>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">Movie avatar = new Movie(&quot;아바타&quot;, Duration.ofMinutes(120), Money.wons(10000), new AmountDiscountPolicy(...));</code>\n        </deckgo-highlight-code>\n<ul>\n<li>생성자 주입</li>\n</ul>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">avatar.setDiscountPolicy(new AmountDiscountPolicy(...));</code>\n        </deckgo-highlight-code>\n<ul>\n<li>setter 주입</li>\n<li>장점: 언제라도 의존 대상을 교체할 수 있다.</li>\n<li>단점: 객체가 올바로 생성되기 위해 어떤 의존성이 필수적인지를 명시적으로 표현할 수 없다.</li>\n</ul>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">avatar.calculateDiscountAmount(screening, new AmountDiscountPolicy(...));</code>\n        </deckgo-highlight-code>\n<ul>\n<li>메서드 주입(메서드 호출 주입)</li>\n<li>메서드가 의존성을 필요로하는 유일한 경우일 때 사용할 수 있다.</li>\n</ul>\n<h3 id=\"-931-숨겨진-의존성은-나쁘다\" style=\"position:relative;\"><a href=\"#-931-%EC%88%A8%EA%B2%A8%EC%A7%84-%EC%9D%98%EC%A1%B4%EC%84%B1%EC%9D%80-%EB%82%98%EC%81%98%EB%8B%A4\" aria-label=\" 931 숨겨진 의존성은 나쁘다 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 9.3.1 숨겨진 의존성은 나쁘다</h3>\n<p>Service Locator 패턴</p>\n<ul>\n<li>의존성을 해결할 수 있는 방법</li>\n<li>Service Locator는 의존성을 해결할 객체들을 보관하는 일종의 저장소</li>\n<li>객체가 직접 Service Locator에게 의존성을 해결해줄 것을 요청</li>\n<li>서비스를 사용하는 코드로부터 서비스가 누구인지(서비스를 구현한 구체 클래스의 타입이 무엇인지), 어디에 있는지(클래스 인스턴스를 어떻게 얻을지)를 몰라도 되게 해준다.</li>\n</ul>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">@NoArgsConstructor(access = AccessLevel.PRIVATE)\npublic class ServiceLocator {\n    \n    private static final ServiceLocator soleInstance = new ServiceLocator();\n    private DiscountPolicy discountPolicy;\n    \n    public static DiscountPolicy discountPolicy() {\n        return soleInstance.discountPolicy;\n    }\n    \n    public static void provide(DiscountPolicy discountPolicy) {\n        soleInstance.discountPolicy = discountPolicy;\n    }\n}</code>\n        </deckgo-highlight-code>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">ServiceLocator.provide(new AmountDiscountPolicy(...));\nMovie avatar = new Movie(&quot;아바타&quot;, Duration.ofMinutes(120), Money.wons(10000));</code>\n        </deckgo-highlight-code>\n<ul>\n<li>위 패턴의 가장 큰 단점은 의존성을 감춘다는 것</li>\n</ul>\n<p>의존성을 구현 내부로 감출 경우</p>\n<ul>\n<li>의존성과 관련된 문제가 컴파일타임이 아닌 런타임에 가서야 발견된다.</li>\n<li>단위 테스트 작성이 어렵다.\n<ul>\n<li>각 단위 테스트는 서로 고립돼야 한다는 기본 원칙 위반</li>\n</ul>\n</li>\n<li>의존성을 이해하기 위해 코드의 내부 구현을 이해할 것을 강요한다.</li>\n<li>의존성의 대상을 설정하는 시점과 의존성이 해결되는 시점을 멀리 떨어트려 놓는다.\n<ul>\n<li>코드를 이해하고 디버깅하기 어렵게 만든다.</li>\n</ul>\n</li>\n</ul>\n<p>의존성 주입을 지원하는 프레임워크를 사용하지 못하는 경우나 깊은 호출 계층에 걸쳐 동일한 객체를 계속해서 전달해야 하는 고통을 견디기 어려운 경우에는 어쩔 수 없이 Service Locator 패턴을 사용하는 것을 고려하라.</p>\n<h2 id=\"-94-의존성-역전-원칙\" style=\"position:relative;\"><a href=\"#-94-%EC%9D%98%EC%A1%B4%EC%84%B1-%EC%97%AD%EC%A0%84-%EC%9B%90%EC%B9%99\" aria-label=\" 94 의존성 역전 원칙 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>📖 9.4 의존성 역전 원칙</h2>\n<h3 id=\"-941-추상화와-의존성-역전\" style=\"position:relative;\"><a href=\"#-941-%EC%B6%94%EC%83%81%ED%99%94%EC%99%80-%EC%9D%98%EC%A1%B4%EC%84%B1-%EC%97%AD%EC%A0%84\" aria-label=\" 941 추상화와 의존성 역전 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 9.4.1 추상화와 의존성 역전</h3>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">public class Movie {\n    private AmountDiscountPolicy discountPolicy;\n}</code>\n        </deckgo-highlight-code>\n<p>의존성은 변경의 전파와 관련된 것이기 때문에 설계는 변경의 영향을 최소화하도록 의존성을 관리해야 한다.</p>\n<ul>\n<li>상위 수준의 클래스는 어떤 식으로든 하위 수준의 클래스에 의존해서는 안 된다.</li>\n<li>대부분의 경우 우리가 재사용하려는 대상은 상위 수준의 클래스다.</li>\n</ul>\n<p>가장 중요한 것은 추상화에 의존하는 것이다.</p>\n<ul>\n<li>모든 의존성의 방향이 추상 클래스나 인터페이스와 같은 추상화를 따라야 한다.</li>\n</ul>\n<p>의존성 역전 원칙(Dependency Inversion Principle, DIP)</p>\n<ol>\n<li>상위 수준의 모듈은 하위 수준의 모듈에 의존해서는 안 된다. 둘 모두 추상화에 의존해야 한다.</li>\n<li>추상화는 구체적인 사항에 의존해서는 안 된다. 구체적인 사항은 추상화에 의존해야 한다.</li>\n<li>역전이라는 단어를 사용한 이유는 DIP를 따르는 설계는 의존성의 방향이 전통적인 절차형 프로그래밍과는 반대 방향으로 나타나기 때문</li>\n</ol>\n<h3 id=\"-942-의존성-역전-원칙과-패키지\" style=\"position:relative;\"><a href=\"#-942-%EC%9D%98%EC%A1%B4%EC%84%B1-%EC%97%AD%EC%A0%84-%EC%9B%90%EC%B9%99%EA%B3%BC-%ED%8C%A8%ED%82%A4%EC%A7%80\" aria-label=\" 942 의존성 역전 원칙과 패키지 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 9.4.2 의존성 역전 원칙과 패키지</h3>\n<p>역전은 의존성의 방향뿐만 아니라 인터페이스의 소유권에도 적용된다.</p>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1025px; \"\n    >\n      <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 37%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAIAAACHqfpvAAAACXBIWXMAABYlAAAWJQFJUiTwAAAA6klEQVQY032Qy26EMAxF5/9/jAVrBKzI8BCjOBmKwUlISNyKzFStKvUsLMvysax744vzPLXW6oKIEHFZlufF/oaInLNEFELI1u1bRsSPN8MwrOtKRFJKpVTf9wCAiCmlfIiZU0ovOaUEAEKI+70fhkFr7b1nZiICkF3XAUB2iMy+0y+ZmUMI3vtw4S/yPMbonM3bzGyMIfoj/+Q8T2MMInZdJ6UEACmlEGLbNkQ0xvwnxxi11lVVFUXRtu00TU3TlGVZ1/U8P6x1L9kEDxs+tc4JK6VyHcdxnmdr7XEc1lrnXAgh9/n/r7Q/AR3lkPPrF582AAAAAElFTkSuQmCC'); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"9.9\"\n        title=\"9.9\"\n        src=\"/static/ee458d231b0407087aa63239dee7bd94/d0143/9.9.png\"\n        srcset=\"/static/ee458d231b0407087aa63239dee7bd94/5a46d/9.9.png 300w,\n/static/ee458d231b0407087aa63239dee7bd94/0a47e/9.9.png 600w,\n/static/ee458d231b0407087aa63239dee7bd94/d0143/9.9.png 1025w\"\n        sizes=\"(max-width: 1025px) 100vw, 1025px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n        decoding=\"async\"\n      />\n    </span></p>\n<ul>\n<li><code>Movie</code>를 다양한 컨텍스트에서 재사용하기 위해서는 불필요한 클래스들이 <code>Movie</code>와 함께 배포해야한 한다.</li>\n<li>불필요한 클래스들을 같은 패키지에 두는 것은 전체적인 빌드 시간을 가파르게 상승시킨다.</li>\n</ul>\n<p>Separated interface 패턴</p>\n<ul>\n<li>추상화를 별도의 독립적인 패키지가 아니라 클라이언트가 속한 패키지에 포함시켜야 한다.</li>\n<li>함게 재사용될 필요가 없는 클래스들은 별도의 독립적인 패키지에 모아햐 한다.</li>\n</ul>\n<p>의존성 역전 원칙에 따라 상위 수준의 협력 흐름을 재사용하기 위해서는 추상화가 제공하는 인터페이스의 소유권 역시 역전시켜야 한다.</p>\n<h2 id=\"-95-유연성에-대한-조언\" style=\"position:relative;\"><a href=\"#-95-%EC%9C%A0%EC%97%B0%EC%84%B1%EC%97%90-%EB%8C%80%ED%95%9C-%EC%A1%B0%EC%96%B8\" aria-label=\" 95 유연성에 대한 조언 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>📖 9.5 유연성에 대한 조언</h2>\n<h3 id=\"-951-유연한-설계는-유연성이-필요할-때만-옳다\" style=\"position:relative;\"><a href=\"#-951-%EC%9C%A0%EC%97%B0%ED%95%9C-%EC%84%A4%EA%B3%84%EB%8A%94-%EC%9C%A0%EC%97%B0%EC%84%B1%EC%9D%B4-%ED%95%84%EC%9A%94%ED%95%A0-%EB%95%8C%EB%A7%8C-%EC%98%B3%EB%8B%A4\" aria-label=\" 951 유연한 설계는 유연성이 필요할 때만 옳다 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 9.5.1 유연한 설계는 유연성이 필요할 때만 옳다</h3>\n<p>유연하고 재사용 가능한 설계가 항상 좋은 것은 아니다.</p>\n<ul>\n<li>설계의 미덕은 단순함과 명확함으로부터 나온다.</li>\n<li>아직 일어나지 않은 변경은 변경이 아니다.</li>\n<li>유연성은 항상 복잡성을 수반한다.</li>\n<li>불필요한 유연성은 불필요한 복잡성을 낳는다.</li>\n</ul>\n<p>복잡성에 대한 걱정보다 유연하고 재사용 가능한 설계의 필요성이 더 크다면 코드의 구조와 실행 구조를 다르게 만들어라.</p>\n<h3 id=\"-952-협력과-책임이-중요하다\" style=\"position:relative;\"><a href=\"#-952-%ED%98%91%EB%A0%A5%EA%B3%BC-%EC%B1%85%EC%9E%84%EC%9D%B4-%EC%A4%91%EC%9A%94%ED%95%98%EB%8B%A4\" aria-label=\" 952 협력과 책임이 중요하다 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 9.5.2 협력과 책임이 중요하다</h3>\n<p>설계를 유연하게 만들기 위해서는 먼저 역할, 책임, 협력에 초점을 맞춰야 한다.</p>\n<ul>\n<li>다양한 컨텍스트에서 협력을 재사용할 필요가 없다면 설계를 유연하게 만들 당위성도 함께 사라진다.</li>\n<li>객체들이 메시지 전송자의 관점에서 동일한 책임을 수행하는지 여부를 판단할 수 없다면 공통의 추상화를 도출할 수 없다.</li>\n<li>동일한 역할을 통해 객체들을 대체 가능하게 만들지 않았다면 협력에 참여하는 객체들을 교체할 필요가 없다.</li>\n<li>중요한 비즈니스 로직을 처리하기 위해 책임을 할당하고 협력의 균형을 맞추는 것이 객체 생성에 관한 책임을 할당하는 것보다 우선이다.</li>\n</ul>\n<p>불필요한 Singleton 패턴은 객체 생성에 관해 너무 이른 시기에 고민하고 결정할 때 도입되는 경향이 있다.</p>\n<p>이번 장에서 설명한 다양한 기법들을 적용하기 전에 역할, 책임, 협력의 모습이 선명하게 그려지지 않는다면 의존성을 관리하는 데 들이는 모든 노력이 물거품이 될 수도 있다는 사실을 명심하라.</p>","excerpt":"📖 9.1 개방-폐쇄 원칙 소프트웨어 개체(클래스, 모듈, 함수 등등)는 확장에 대해 열려 있어야 하고, 수정에 대해서는 닫혀 있어야 한다. 확장에 대해 열려 있다: 애플리케이션의 요구사항이 변경될 때 이 변경에 맞게 새로운 '동작'을 추가해서 애플리케이션의 기능을 확장할 수 있다. 수정에 대해 닫혀 있다: 기존의 '코드'를 수정하지 않고도 애플리케이션의 동작을 추가하거나 변경할 수 있다. 🔖 9.1.…","fields":{"slug":"/backend/object/chapter9/유연한_설계/"},"frontmatter":{"title":"Object - 9장 유연한 설계","thumbnail":{"childImageSharp":{"fluid":{"src":"/static/dd0e1990925e942697544e2bdcd9332e/9b73b/object.png"}}},"draft":false,"category":"Back-End","tags":["java"],"date":"October 22, 2023"}},"previous":{"id":"45d49f32-c00f-5da9-8b11-e6b443efa26c","html":"<p>상속</p>\n<ul>\n<li>부모 클래스와 자식 클래스 사이의 의존성은 컴파일타임에 해결</li>\n<li>is-a 관계</li>\n<li>클래스 사이의 정적 관계</li>\n</ul>\n<p>합성</p>\n<ul>\n<li>부모 클래스와 자식 클래스 사이의 의존성은 런타임에 해결</li>\n<li>has-a 관계</li>\n<li>객체 사이의 동적 관계</li>\n</ul>\n<p>코드 재사용을 위해서는 객체 합성이 클래스 상속보다 더 좋은 방법이다.</p>\n<h2 id=\"-111-상속을-합성으로-변경하기\" style=\"position:relative;\"><a href=\"#-111-%EC%83%81%EC%86%8D%EC%9D%84-%ED%95%A9%EC%84%B1%EC%9C%BC%EB%A1%9C-%EB%B3%80%EA%B2%BD%ED%95%98%EA%B8%B0\" aria-label=\" 111 상속을 합성으로 변경하기 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>📖 11.1 상속을 합성으로 변경하기</h2>\n<p>상속을 남용할 때 직면하는 세 가지 문제</p>\n<ol>\n<li>불필요한 인터페이스 상속 문제</li>\n<li>메서드 오버라이딩의 오작용 문제</li>\n<li>부모 클래스와 자식 클래스의 동시 수정 문제</li>\n</ol>\n<h3 id=\"-1111-불필요한-인터페이스-상속-문제-javautilproperties와-javautilstack\" style=\"position:relative;\"><a href=\"#-1111-%EB%B6%88%ED%95%84%EC%9A%94%ED%95%9C-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4-%EC%83%81%EC%86%8D-%EB%AC%B8%EC%A0%9C-javautilproperties%EC%99%80-javautilstack\" aria-label=\" 1111 불필요한 인터페이스 상속 문제 javautilproperties와 javautilstack permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 11.1.1 불필요한 인터페이스 상속 문제: java.util.Properties와 java.util.Stack</h3>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">public class Properties {\n    private Hashtable&lt;String, String&gt; properties = new Hashtable&lt;&gt;();\n\n    public String setProperty(String key, String value) {\n        return properties.put(key, value);\n    }\n\n    public String getProperty(String key) {\n        return properties.get(key);\n    }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>더 이상 불필요한 <code>Hashtable</code>의 오퍼레이션들이 퍼블릭 인터페이스를 오염시키지 않는다.</li>\n<li><code>Stack</code> 또한 <code>Vector</code>의 인스턴스 변수를 <code>Stack</code> 클래스의 인스턴스 변수로 선언함으로써 합성 관계로 변경할 수 있다.</li>\n</ul>\n<h3 id=\"-1112-메서드-오버라이딩의-오작용-문제-instrumentedhashset\" style=\"position:relative;\"><a href=\"#-1112-%EB%A9%94%EC%84%9C%EB%93%9C-%EC%98%A4%EB%B2%84%EB%9D%BC%EC%9D%B4%EB%94%A9%EC%9D%98-%EC%98%A4%EC%9E%91%EC%9A%A9-%EB%AC%B8%EC%A0%9C-instrumentedhashset\" aria-label=\" 1112 메서드 오버라이딩의 오작용 문제 instrumentedhashset permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 11.1.2 메서드 오버라이딩의 오작용 문제: InstrumentedHashSet</h3>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">@RequiredArgsConstructor\npublic class InstrumentedHashSet&lt;E&gt; implements Set&lt;E&gt; {\n    \n    private static final int addCount = 0;\n    \n    private final Set&lt;E&gt; set;\n\n    ... // Overriding\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li><code>HashSet</code>에 대한 구현 결합도는 제거하면서 퍼블릭 인터페이스는 그대로 상속</li>\n</ul>\n<p>포워딩</p>\n<ul>\n<li><code>Set</code>의 오버레이션을 오버라이딩한 인스턴스 메서드에서 내부의 <code>HashSet</code> 인스턴스에게 동일한 메서드 호출을 그대로 전달</li>\n<li>동일한 메서드를 호출하기 위해 추가된 메서드를 <strong>포워딩 메서드</strong>라고 부른다.</li>\n<li>기존 클래스의 인터페이스를 그대로 외부에 제공하면서 구현에 대한 결합 없이 일부 작동 방식을 변경하고 싶은 경우에 유용한 기법</li>\n</ul>\n<h3 id=\"-1113-부모-클래스와-자식-클래스의-동시-수정-문제-personalplaylist\" style=\"position:relative;\"><a href=\"#-1113-%EB%B6%80%EB%AA%A8-%ED%81%B4%EB%9E%98%EC%8A%A4%EC%99%80-%EC%9E%90%EC%8B%9D-%ED%81%B4%EB%9E%98%EC%8A%A4%EC%9D%98-%EB%8F%99%EC%8B%9C-%EC%88%98%EC%A0%95-%EB%AC%B8%EC%A0%9C-personalplaylist\" aria-label=\" 1113 부모 클래스와 자식 클래스의 동시 수정 문제 personalplaylist permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 11.1.3 부모 클래스와 자식 클래스의 동시 수정 문제: PersonalPlaylist</h3>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">public class PersonalPlaylist {\n\n    private Playlist playlist = new Playlist();\n\n    public void append(Song song) {\n        playlist.append(song);\n    }\n\n    public void remove(Song song) {\n        playlist.getTracks().remove(song);\n        playlist.getSingers().remove(song.getSinger());\n    }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>향후에 <code>Playlist</code>의 내부 구현을 변경하더라도 파급효과를 최대한 <code>PersonalPlaylist</code> 내부로 캡슐화할 수 있다.</li>\n</ul>\n<p>몽키 패치</p>\n<ul>\n<li>현재 실행 중인 환경에만 영향을 미치도록 지역적으로 코드를 수정하거나 확장하는 것</li>\n</ul>\n<h2 id=\"-112-상속으로-인한-조합의-폭발적인-증가\" style=\"position:relative;\"><a href=\"#-112-%EC%83%81%EC%86%8D%EC%9C%BC%EB%A1%9C-%EC%9D%B8%ED%95%9C-%EC%A1%B0%ED%95%A9%EC%9D%98-%ED%8F%AD%EB%B0%9C%EC%A0%81%EC%9D%B8-%EC%A6%9D%EA%B0%80\" aria-label=\" 112 상속으로 인한 조합의 폭발적인 증가 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>📖 11.2 상속으로 인한 조합의 폭발적인 증가</h2>\n<ol>\n<li>하나의 기능을 추가하거나 수정하기 위해 불필요하게 많은 수의 클래스를 추가하거나 수정해야 한다.</li>\n<li>단일 상속만 지원하는 언어에서는 상속으로 인해 오히려 중복 코드의 양이 늘어날 수 있다.</li>\n</ol>\n<h3 id=\"-1121-기본-정책과-부가-정책-조합하기\" style=\"position:relative;\"><a href=\"#-1121-%EA%B8%B0%EB%B3%B8-%EC%A0%95%EC%B1%85%EA%B3%BC-%EB%B6%80%EA%B0%80-%EC%A0%95%EC%B1%85-%EC%A1%B0%ED%95%A9%ED%95%98%EA%B8%B0\" aria-label=\" 1121 기본 정책과 부가 정책 조합하기 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 11.2.1 기본 정책과 부가 정책 조합하기</h3>\n<p>기본 정책</p>\n<ul>\n<li>가입자의 통화 정보 기반</li>\n<li>일반 요금제, 심야 할인 요금제</li>\n</ul>\n<p>부가 정책</p>\n<ul>\n<li>통화량과 무관하게 기본 정책에 선택적으로 추가할 수 있는 요금 방식</li>\n<li>세금 정책, 기본 요금 할인 정책</li>\n<li>기본 정책의 계산 결과에 적용</li>\n<li>선택적으로 적용 가능</li>\n<li>조합 가능</li>\n<li>임의의 순서로 적용 가능</li>\n</ul>\n<h3 id=\"-1122-상속을-이용해서-기본-정책-구현하기\" style=\"position:relative;\"><a href=\"#-1122-%EC%83%81%EC%86%8D%EC%9D%84-%EC%9D%B4%EC%9A%A9%ED%95%B4%EC%84%9C-%EA%B8%B0%EB%B3%B8-%EC%A0%95%EC%B1%85-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0\" aria-label=\" 1122 상속을 이용해서 기본 정책 구현하기 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 11.2.2 상속을 이용해서 기본 정책 구현하기</h3>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">@RequiredArgsConstructor\npublic abstract class Phone {\n\n    private List&lt;Call&gt; calls = new ArrayList&lt;&gt;();\n\n    public Money calculateFee() {\n        Money result = Money.ZERO;\n\n        for (Call call : calls) {\n            result = result.plus(calculateCallFee(call));\n        }\n\n        return result;\n    }\n\n    protected abstract Money calculateCallFee(Call call);\n}</code>\n        </deckgo-highlight-code>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">@RequiredArgsConstructor\npublic class RegularPhone extends Phone {\n\n    private final Money amount;\n\n    private final Duration seconds;\n\n    @Override\n    protected Money calculateCallFee(Call call) {\n        return amount.times((double) call.getDuration().getSeconds() / seconds.getSeconds());\n    }\n}</code>\n        </deckgo-highlight-code>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">@RequiredArgsConstructor\npublic class NightlyDiscountPhone extends Phone {\n\n    private static final int LATE_NIGHT_HOUR = 22;\n\n    private final Money nightlyAmount;\n\n    private final Money regularAmount;\n\n    private final Duration seconds;\n\n    @Override\n    protected Money calculateCallFee(Call call) {\n        if (call.getFrom().getHour() &gt;= LATE_NIGHT_HOUR) {\n            return nightlyAmount.times((double) call.getDuration().getSeconds() / seconds.getSeconds());\n        }\n        return regularAmount.times((double) call.getDuration().getSeconds() / seconds.getSeconds());\n    }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>기본 정책으로만 요금 계산</li>\n</ul>\n<h3 id=\"-1123-기본-정책에-세금-정책-조합하기\" style=\"position:relative;\"><a href=\"#-1123-%EA%B8%B0%EB%B3%B8-%EC%A0%95%EC%B1%85%EC%97%90-%EC%84%B8%EA%B8%88-%EC%A0%95%EC%B1%85-%EC%A1%B0%ED%95%A9%ED%95%98%EA%B8%B0\" aria-label=\" 1123 기본 정책에 세금 정책 조합하기 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 11.2.3 기본 정책에 세금 정책 조합하기</h3>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">public class TaxableRegularPhone extends RegularPhone {\n\n    private final double taxRate;\n\n    public TaxableRegularPhone(Money amount, Duration seconds, double taxRate) {\n        super(amount, seconds);\n        this.taxRate = taxRate;\n    }\n\n    @Override\n    public Money calculateFee() {\n        Money fee = super.calculateFee();\n        return fee.plus(fee.times(taxRate));\n    }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>결합도가 높아진다.</li>\n</ul>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">@RequiredArgsConstructor\npublic abstract class Phone {\n\n    private List&lt;Call&gt; calls = new ArrayList&lt;&gt;();\n\n    public Money calculateFee() {\n        Money result = Money.ZERO;\n\n        for (Call call : calls) {\n            result = result.plus(calculateCallFee(call));\n        }\n\n        return afterCalculated(result);\n    }\n\n    protected abstract Money calculateCallFee(Call call);\n    \n    protected abstract Money afterCalculated(Money fee);\n}\n</code>\n        </deckgo-highlight-code>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">@RequiredArgsConstructor\npublic class RegularPhone extends Phone {\n\n    private final Money amount;\n\n    private final Duration seconds;\n\n    @Override\n    protected Money calculateCallFee(Call call) {\n        return amount.times((double) call.getDuration().getSeconds() / seconds.getSeconds());\n    }\n\n    @Override\n    protected Money afterCalculated(Money fee) {\n        return fee;\n    }\n}</code>\n        </deckgo-highlight-code>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">@RequiredArgsConstructor\npublic class NightlyDiscountPhone extends Phone {\n\n    private static final int LATE_NIGHT_HOUR = 22;\n\n    private final Money nightlyAmount;\n\n    private final Money regularAmount;\n\n    private final Duration seconds;\n\n    @Override\n    protected Money calculateCallFee(Call call) {\n        if (call.getFrom().getHour() &gt;= LATE_NIGHT_HOUR) {\n            return nightlyAmount.times((double) call.getDuration().getSeconds() / seconds.getSeconds());\n        }\n        return regularAmount.times((double) call.getDuration().getSeconds() / seconds.getSeconds());\n    }\n\n    @Override\n    protected Money afterCalculated(Money fee) {\n        return fee;\n    }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>부모 클래스에 추상 메서드를 추가하면 꽤나 번거로워진다.</li>\n</ul>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">@RequiredArgsConstructor\npublic abstract class Phone {\n\n    private List&lt;Call&gt; calls = new ArrayList&lt;&gt;();\n\n    public Money calculateFee() {\n        Money result = Money.ZERO;\n\n        for (Call call : calls) {\n            result = result.plus(calculateCallFee(call));\n        }\n\n        return afterCalculated(result);\n    }\n\n    protected Money afterCalculated(Money fee) {\n        return fee;\n    }\n\n    protected abstract Money calculateCallFee(Call call);\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>기본 구현을 함께 제공하면 오버라이딩할 필요가 없다.</li>\n<li>훅 메서드(hook method)</li>\n<li>추상 메서드와 동일하게 자식 클래스에서 오버라이딩할 의도로 메서드를 추가했지만 편의를 위해 기본 구현을 제공하는 메서드</li>\n</ul>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">public class TaxableRegularPhone extends RegularPhone {\n\n    private final double taxRate;\n\n    public TaxableRegularPhone(Money amount, Duration seconds, double taxRate) {\n        super(amount, seconds);\n        this.taxRate = taxRate;\n    }\n\n    @Override\n    protected Money afterCalculated(Money fee) {\n        return fee.plus(fee.times(taxRate));\n    }\n}</code>\n        </deckgo-highlight-code>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">public class TaxableNightlyDiscountPhone extends NightlyDiscountPhone {\n\n    private final double taxRate;\n\n    public TaxableNightlyDiscountPhone(Money nightlyAmount, Money regularAmount, Duration seconds, double taxRate) {\n        super(nightlyAmount, regularAmount, seconds);\n        this.taxRate = taxRate;\n    }\n\n    @Override\n    protected Money afterCalculated(Money fee) {\n        return fee.plus(fee.times(taxRate));\n    }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>부모 클래스의 이름을 제외하면 대부분의 코드가 거의 동일</li>\n</ul>\n<h3 id=\"-1124-기본-정책에-기본-요금-할인-정책-조합하기\" style=\"position:relative;\"><a href=\"#-1124-%EA%B8%B0%EB%B3%B8-%EC%A0%95%EC%B1%85%EC%97%90-%EA%B8%B0%EB%B3%B8-%EC%9A%94%EA%B8%88-%ED%95%A0%EC%9D%B8-%EC%A0%95%EC%B1%85-%EC%A1%B0%ED%95%A9%ED%95%98%EA%B8%B0\" aria-label=\" 1124 기본 정책에 기본 요금 할인 정책 조합하기 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 11.2.4 기본 정책에 기본 요금 할인 정책 조합하기</h3>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">public class RateDiscountableRegularPhone extends RegularPhone {\n\n    private final Money discountAmount;\n\n    public RateDiscountableRegularPhone(Money amount, Duration seconds, Money discountAmount) {\n        super(amount, seconds);\n        this.discountAmount = discountAmount;\n    }\n\n    @Override\n    protected Money afterCalculated(Money fee) {\n        return fee.minus(discountAmount);\n    }\n}</code>\n        </deckgo-highlight-code>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">public class RateDiscountableNightlyDiscountPhone extends NightlyDiscountPhone {\n\n    private final Money discountAmount;\n\n    public RateDiscountableNightlyDiscountPhone(Money nightlyAmount, Money regularAmount, Duration seconds, Money discountAmount) {\n        super(nightlyAmount, regularAmount, seconds);\n        this.discountAmount = discountAmount;\n    }\n\n    @Override\n    protected Money afterCalculated(Money fee) {\n        return fee.minus(discountAmount);\n    }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>또, 중복 코드가 추가되었다.</li>\n</ul>\n<h3 id=\"-1125-중복-코드의-덫에-걸리다\" style=\"position:relative;\"><a href=\"#-1125-%EC%A4%91%EB%B3%B5-%EC%BD%94%EB%93%9C%EC%9D%98-%EB%8D%AB%EC%97%90-%EA%B1%B8%EB%A6%AC%EB%8B%A4\" aria-label=\" 1125 중복 코드의 덫에 걸리다 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 11.2.5 중복 코드의 덫에 걸리다</h3>\n<p>상속을 이용한 해결 방법은 모든 가능한 조합별로 자식 클래스를 하나씩 추가하는 것이다.</p>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">public class TaxableAndRateDiscountableRegularPhone extends TaxableRegularPhone {\n\n    private final Money discountAmount;\n\n    public TaxableAndRateDiscountableRegularPhone(Money amount, Duration seconds, double taxRate, Money discountAmount) {\n        super(amount, seconds, taxRate);\n        this.discountAmount = discountAmount;\n    }\n\n    @Override\n    protected Money afterCalculated(Money fee) {\n        return super.afterCalculated(fee).minus(discountAmount);\n    }\n}</code>\n        </deckgo-highlight-code>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">public class RateDiscountableAndTaxableRegularPhone extends RateDiscountableRegularPhone {\n\n    private final double taxRate;\n\n    public RateDiscountableAndTaxableRegularPhone(Money amount, Duration seconds, Money discountAmount, double taxRate) {\n        super(amount, seconds, discountAmount);\n        this.taxRate = taxRate;\n    }\n\n    @Override\n    protected Money afterCalculated(Money fee) {\n        return super.afterCalculated(fee).plus(fee.times(taxRate));\n    }\n}</code>\n        </deckgo-highlight-code>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">public class TaxableAndDiscountableNightlyDiscountPhone extends TaxableNightlyDiscountPhone {\n\n    private final Money discountAmount;\n\n    public TaxableAndDiscountableNightlyDiscountPhone(Money nightlyAmount, Money regularAmount, Duration seconds, double taxRate, Money discountAmount) {\n        super(nightlyAmount, regularAmount, seconds, taxRate);\n        this.discountAmount = discountAmount;\n    }\n\n    @Override\n    protected Money afterCalculated(Money fee) {\n        return super.afterCalculated(fee).minus(discountAmount);\n    }\n}</code>\n        </deckgo-highlight-code>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">public class RateDiscountableAndTaxableNightlyDiscountPhone extends RateDiscountableNightlyDiscountPhone {\n\n    private final double taxRate;\n\n    public RateDiscountableAndTaxableNightlyDiscountPhone(Money nightlyAmount, Money regularAmount, Duration seconds, Money discountAmount, double taxRate) {\n        super(nightlyAmount, regularAmount, seconds, discountAmount);\n        this.taxRate = taxRate;\n    }\n\n    @Override\n    protected Money afterCalculated(Money fee) {\n        return super.afterCalculated(fee).plus(fee.times(taxRate));\n    }\n}\n</code>\n        </deckgo-highlight-code>\n<ul>\n<li><strong>클래스 폭발(class explosion)</strong> 또는 <strong>조합의 폭발(combinational explosion)</strong></li>\n<li>자식 클래스가 부모 클래스의 구현에 강하게 결합되도록 강요하는 상속의 근본적인 한계 때문에 발생</li>\n<li>기능을 추가할 때뿐만 아니라 기능을 수정할 때도 문제가 됨.</li>\n</ul>\n<h2 id=\"-113-합성-관계로-변경하기\" style=\"position:relative;\"><a href=\"#-113-%ED%95%A9%EC%84%B1-%EA%B4%80%EA%B3%84%EB%A1%9C-%EB%B3%80%EA%B2%BD%ED%95%98%EA%B8%B0\" aria-label=\" 113 합성 관계로 변경하기 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>📖 11.3 합성 관계로 변경하기</h2>\n<p>합성을 사용하면 구현이 아닌 퍼블릭 인터페이스에 대해서만 의존할 수 있기 때문에 런타임에 객체의 관계를 변경할 수 있다.</p>\n<h3 id=\"-1131-기본-정책-합성하기\" style=\"position:relative;\"><a href=\"#-1131-%EA%B8%B0%EB%B3%B8-%EC%A0%95%EC%B1%85-%ED%95%A9%EC%84%B1%ED%95%98%EA%B8%B0\" aria-label=\" 1131 기본 정책 합성하기 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 11.3.1 기본 정책 합성하기</h3>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">public interface RatePolicy {\n    Money calculateFee(Phone phone);\n}</code>\n        </deckgo-highlight-code>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">public abstract class BasicRatePolicy implements RatePolicy{\n\n    @Override\n    public Money calculateFee(Phone phone) {\n        Money result = Money.ZERO;\n\n        for (Call call : phone.getCalls()) {\n            result.plus(calculateCallFee(call));\n        }\n\n        return result;\n    }\n\n    protected abstract Money calculateCallFee(Call call);\n}</code>\n        </deckgo-highlight-code>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">@RequiredArgsConstructor\npublic class RegularPolicy extends BasicRatePolicy {\n\n    private final Money amount;\n    private final Duration seconds;\n\n    @Override\n    protected Money calculateCallFee(Call call) {\n        return amount.times((double) call.getDuration().getSeconds() / seconds.getSeconds());\n    }\n}</code>\n        </deckgo-highlight-code>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">@RequiredArgsConstructor\npublic class NightlyDiscountPolicy extends BasicRatePolicy {\n\n    private static final int LATE_NIGHT_HOUR = 22;\n\n    private final Money nightlyAmount;\n    private final Money regularAmount;\n    private final Duration seconds;\n\n    @Override\n    protected Money calculateCallFee(Call call) {\n        if (call.getFrom().getHour() &gt;= LATE_NIGHT_HOUR) {\n            return nightlyAmount.times((double) call.getDuration().getSeconds() / seconds.getSeconds());\n        }\n        return regularAmount.times((double) call.getDuration().getSeconds() / seconds.getSeconds());\n    }\n}</code>\n        </deckgo-highlight-code>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">@RequiredArgsConstructor\npublic class Phone {\n\n    private final RatePolicy ratePolicy;\n\n    @Getter\n    private List&lt;Call&gt; calls = new ArrayList&lt;&gt;();\n\n    public Money calculateFee() {\n        return ratePolicy.calculateFee(this);\n    }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>다양한 종류의 객체와 협력하기 위해 합성 관계를 사용하는 경우에는 합성하는 객체의 타입을 인터페이스나 추상 클래스로 선언하고 의존성 주입을 사용해 런타임에 필요한 객체를 설정할 수 있도록 구현하는 것이 일반적이다.</li>\n</ul>\n<h3 id=\"-1132-부가-정책-적용하기\" style=\"position:relative;\"><a href=\"#-1132-%EB%B6%80%EA%B0%80-%EC%A0%95%EC%B1%85-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0\" aria-label=\" 1132 부가 정책 적용하기 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 11.3.2 부가 정책 적용하기</h3>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">@RequiredArgsConstructor\npublic abstract class AdditionalRatePolicy implements RatePolicy {\n\n    private final RatePolicy next;\n\n    @Override\n    public Money calculateFee(Phone phone) {\n        Money fee = next.calculateFee(phone);\n        return afterCalculated(fee);\n    }\n\n    protected abstract Money afterCalculated(Money fee);\n}</code>\n        </deckgo-highlight-code>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">public class TaxablePolicy extends AdditionalRatePolicy {\n    \n    private final double taxRatio;\n\n    public TaxablePolicy(RatePolicy next, double taxRatio) {\n        super(next);\n        this.taxRatio = taxRatio;\n    }\n\n    @Override\n    protected Money afterCalculated(Money fee) {\n        return fee.plus(fee.times(taxRatio));\n    }\n}</code>\n        </deckgo-highlight-code>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">public class RateDiscountablePolicy extends AdditionalRatePolicy {\n\n    private final Money discountAmount;\n\n    public RateDiscountablePolicy(RatePolicy next, Money discountAmount) {\n        super(next);\n        this.discountAmount = discountAmount;\n    }\n\n    @Override\n    protected Money afterCalculated(Money fee) {\n        return fee.minus(discountAmount);\n    }\n}</code>\n        </deckgo-highlight-code>\n<h3 id=\"-1133-기본-정책과-부가-정책-합성하기\" style=\"position:relative;\"><a href=\"#-1133-%EA%B8%B0%EB%B3%B8-%EC%A0%95%EC%B1%85%EA%B3%BC-%EB%B6%80%EA%B0%80-%EC%A0%95%EC%B1%85-%ED%95%A9%EC%84%B1%ED%95%98%EA%B8%B0\" aria-label=\" 1133 기본 정책과 부가 정책 합성하기 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 11.3.3 기본 정책과 부가 정책 합성하기</h3>\n<p><code>Phone phone = new Phone(new taxablePolicy(0.05, new RegularPolicy(...)))</code></p>\n<ul>\n<li>객체를 조합하고 사용하는 방식이 상속을 사용한 방식보다 더 예측 가능하고 일관성 있다.</li>\n</ul>\n<h3 id=\"-1134-새로운-정책-추가하기\" style=\"position:relative;\"><a href=\"#-1134-%EC%83%88%EB%A1%9C%EC%9A%B4-%EC%A0%95%EC%B1%85-%EC%B6%94%EA%B0%80%ED%95%98%EA%B8%B0\" aria-label=\" 1134 새로운 정책 추가하기 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 11.3.4 새로운 정책 추가하기</h3>\n<ul>\n<li>오직 하나의 클래스만 추가하고 런타임에 필요한 정책들을 조합해서 원하는 기능을 얻을 수 있다.</li>\n<li>요구사항을 변경할 때 오직 하나의 클래스만 수정해도 된다.\n<ul>\n<li>변경 후의 설계는 단일 책임 원칙을 준수하고 있다.</li>\n</ul>\n</li>\n</ul>\n<h3 id=\"-1135-객체-합성이-클래스-상속보다-더-좋은-방법이다\" style=\"position:relative;\"><a href=\"#-1135-%EA%B0%9D%EC%B2%B4-%ED%95%A9%EC%84%B1%EC%9D%B4-%ED%81%B4%EB%9E%98%EC%8A%A4-%EC%83%81%EC%86%8D%EB%B3%B4%EB%8B%A4-%EB%8D%94-%EC%A2%8B%EC%9D%80-%EB%B0%A9%EB%B2%95%EC%9D%B4%EB%8B%A4\" aria-label=\" 1135 객체 합성이 클래스 상속보다 더 좋은 방법이다 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 11.3.5 객체 합성이 클래스 상속보다 더 좋은 방법이다</h3>\n<ul>\n<li>상속은 부모 클래스의 세부적인 구현에 자식 클래스를 강하게 결합시키기 때문에 코드의 진화를 방해한다.</li>\n<li>상속이 구현을 재사용하는 데 비해 합성은 객체의 인터페이스를 재사용한다.</li>\n<li>상속의 종류: 구현 상속 / 인터페이스 상속\n<ul>\n<li>이번 장에서 살펴본 상속의 단점은 구현 상속</li>\n</ul>\n</li>\n</ul>\n<h2 id=\"-114-믹스인\" style=\"position:relative;\"><a href=\"#-114-%EB%AF%B9%EC%8A%A4%EC%9D%B8\" aria-label=\" 114 믹스인 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>📖 11.4 믹스인</h2>\n<ul>\n<li>객체를 생성할 때 코드 일부를 클래스 안에 섞어 넣어 재사용하는 기법을 가리키는 용어</li>\n<li>코드를 다른 코드 안에 섞어 넣기 위한 방법</li>\n<li>상속이 클래스와 클래스 사이의 관계를 고정시키는 데 비해 믹스인은 유연하게 관계를 재구성할 수 있다.</li>\n<li><a href=\"https://bottleh.netlify.app/backend/effective-java/4%EC%9E%A5_%ED%81%B4%EB%9E%98%EC%8A%A4%EC%99%80_%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4/#%EC%95%84%EC%9D%B4%ED%85%9C20-%EC%B6%94%EC%83%81-%ED%81%B4%EB%9E%98%EC%8A%A4%EB%B3%B4%EB%8B%A4%EB%8A%94-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4%EB%A5%BC-%EC%9A%B0%EC%84%A0%ED%95%98%EB%9D%BC\">이펙티브 자바 아이템20</a></li>\n<li>java에서 인터페이스는 믹스인 정의에 안성맞춤이지만, 추상 클래스는 믹스인을 정의할 수 없다.</li>\n</ul>\n<h2 id=\"-1141-쌓을-수-있는-변경\" style=\"position:relative;\"><a href=\"#-1141-%EC%8C%93%EC%9D%84-%EC%88%98-%EC%9E%88%EB%8A%94-%EB%B3%80%EA%B2%BD\" aria-label=\" 1141 쌓을 수 있는 변경 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>📖 11.4.1 쌓을 수 있는 변경</h2>\n<ul>\n<li>믹스인은 대상 클래스의 자식 클래스처럼 사용될 용도로 만들어지는 것</li>\n<li>믹스인을 추상 서브클래스(abstract subclass)라고 부르기도 한다.</li>\n<li>쌓을 수 있는 변경: 믹스인을 사용하면 특정한 클래스에 대한 변경 또는 확장을 독립적으로 구현한 후 필요한 시점에 차례대로 추가할 수 있다.</li>\n</ul>","excerpt":"상속 부모 클래스와 자식 클래스 사이의 의존성은 컴파일타임에 해결 is-a 관계 클래스 사이의 정적 관계 합성 부모 클래스와 자식 클래스 사이의 의존성은 런타임에 해결 has-a 관계 객체 사이의 동적 관계 코드 재사용을 위해서는 객체 합성이 클래스 상속보다 더 좋은 방법이다. 📖 11.1 상속을 합성으로 변경하기 상속을 남용할 때 직면하는 세 가지 문제 불필요한 인터페이스 상속 문제 메서드 오버라이딩의 오작용 문제 부모 클래스와 자식 클래스의 동시 수정 문제 🔖 11.1.…","fields":{"slug":"/backend/object/chapter11/합성과_유연한_설계/"},"frontmatter":{"title":"Object - 11장 합성과 유연한 설계","thumbnail":{"childImageSharp":{"fluid":{"src":"/static/dd0e1990925e942697544e2bdcd9332e/9b73b/object.png"}}},"draft":false,"category":"Back-End","tags":["java"],"date":"November 05, 2023"}},"node":{"id":"a37febe6-8388-5efc-91d5-5fac75bdd784","html":"<p>코드를 재사용하려는 강력한 동기 이면에는 중복된 코드를 제거하려는 욕망이 숨어 있다.</p>\n<h2 id=\"-101-상속과-중복-코드\" style=\"position:relative;\"><a href=\"#-101-%EC%83%81%EC%86%8D%EA%B3%BC-%EC%A4%91%EB%B3%B5-%EC%BD%94%EB%93%9C\" aria-label=\" 101 상속과 중복 코드 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>📖 10.1 상속과 중복 코드</h2>\n<p>중복 코드는 사람들의 마음속에 의심과 불신의 씨앗을 뿌린다.</p>\n<h3 id=\"-1011-dry-원칙\" style=\"position:relative;\"><a href=\"#-1011-dry-%EC%9B%90%EC%B9%99\" aria-label=\" 1011 dry 원칙 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 10.1.1 DRY 원칙</h3>\n<p><strong>중복 코드는 변경을 방해한다.</strong> 이것이 중복 코드를 제거해야 하는 가장 큰 이유다.</p>\n<ul>\n<li>중복 여부를 판단하는 기준은 변경이다.</li>\n<li>요구사항이 변경됐을 때 두 코드를 함께 수정해야 한다면 중복</li>\n<li>DRY 원칙을 따르자.\n<ul>\n<li>\n<blockquote>\n<p>Don't Repeat Yourself</p>\n</blockquote>\n</li>\n<li>한 번, 단 한 번의 원칙</li>\n<li>단일 지점 제어 원칙</li>\n<li>모든 지식은 시스템 내에서 단일하고, 애매하지 않고, 정말로 믿을 만한 표현 양식을 가져야 한다.</li>\n</ul>\n</li>\n</ul>\n<h3 id=\"-1012-중복과-변경\" style=\"position:relative;\"><a href=\"#-1012-%EC%A4%91%EB%B3%B5%EA%B3%BC-%EB%B3%80%EA%B2%BD\" aria-label=\" 1012 중복과 변경 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 10.1.2 중복과 변경</h3>\n<h4 id=\"-중복-코드-살펴보기\" style=\"position:relative;\"><a href=\"#-%EC%A4%91%EB%B3%B5-%EC%BD%94%EB%93%9C-%EC%82%B4%ED%8E%B4%EB%B3%B4%EA%B8%B0\" aria-label=\" 중복 코드 살펴보기 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🎈 중복 코드 살펴보기</h4>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">@RequiredArgsConstructor\npublic class Call {\n    \n    @Getter\n    private final LocalDateTime from;\n    \n    private final LocalDateTime to;\n\n    public Duration getDuration() {\n        return Duration.between(from, to);\n    }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>개별 통화 기간울 저장하는 Class</li>\n</ul>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">@Getter\n@RequiredArgsConstructor\npublic class Phone {\n\n    private final Money amount;\n\n    private final Duration seconds;\n\n    private List&lt;Call&gt; calls = new ArrayList&lt;&gt;();\n\n    public void call(Call call) {\n        calls.add(call);\n    }\n\n    public Money calculateFee() {\n        Money result = Money.ZERO;\n\n        for (Call call : calls) {\n            result = result.plus(amount.times((double) call.getDuration().getSeconds() / seconds.getSeconds()));\n        }\n\n        return result;\n    }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>위 요금제는 일반 요금제이다.</li>\n<li>여기서, 심야 할인 요금제(밤 10시 이후의 통화에 대해 할인)가 추가 되었다.</li>\n</ul>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">@RequiredArgsConstructor\npublic class NightlyDiscountPhone {\n    \n    private static final int LATE_NIGHT_HOUR = 22;\n\n    private final Money nightlyAmount;\n\n    private final Money regularAmount;\n\n    private final Duration seconds;\n\n    private List&lt;Call&gt; calls = new ArrayList&lt;&gt;();\n\n    public Money calculateCallFee() {\n        Money result = Money.ZERO;\n\n        for (Call call : calls) {\n            if (call.getFrom().getHour() &gt;= LATE_NIGHT_HOUR) {\n                result = result.plus(nightlyAmount.times((double) call.getDuration().getSeconds() / seconds.getSeconds()));\n            } else {\n                result = result.plus(regularAmount.times((double) call.getDuration().getSeconds() / seconds.getSeconds()));\n            }\n        }\n\n        return result;\n    }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>중복 코드가 생겼다!</li>\n</ul>\n<h4 id=\"-중복-코드-수정하기\" style=\"position:relative;\"><a href=\"#-%EC%A4%91%EB%B3%B5-%EC%BD%94%EB%93%9C-%EC%88%98%EC%A0%95%ED%95%98%EA%B8%B0\" aria-label=\" 중복 코드 수정하기 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🎈 중복 코드 수정하기</h4>\n<p>새로운 요구사항: 통화 요금에 부과할 세금 계산</p>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">@Getter\n@RequiredArgsConstructor\npublic class Phone {\n\n    private final Money amount;\n\n    private final Duration seconds;\n    \n    private final double taxRate;\n\n    private List&lt;Call&gt; calls = new ArrayList&lt;&gt;();\n\n    public void call(Call call) {\n        calls.add(call);\n    }\n\n    public Money calculateFee() {\n        Money result = Money.ZERO;\n\n        for (Call call : calls) {\n            result = result.plus(amount.times((double) call.getDuration().getSeconds() / seconds.getSeconds()));\n        }\n\n        return result.plus(result.times(taxRate));\n    }\n}</code>\n        </deckgo-highlight-code>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">@RequiredArgsConstructor\npublic class NightlyDiscountPhone {\n\n    private static final int LATE_NIGHT_HOUR = 22;\n\n    private final Money nightlyAmount;\n    \n    private final Money regularAmount;\n    \n    private final Duration seconds;\n    \n    private final double taxRate;\n    \n    private List&lt;Call&gt; calls = new ArrayList&lt;&gt;();\n\n    public Money calculateCallFee() {\n        Money result = Money.ZERO;\n\n        for (Call call : calls) {\n            if (call.getFrom().getHour() &gt;= LATE_NIGHT_HOUR) {\n                result = result.plus(nightlyAmount.times((double) call.getDuration().getSeconds() / seconds.getSeconds()));\n            } else {\n                result = result.plus(regularAmount.times((double) call.getDuration().getSeconds() / seconds.getSeconds()));\n            }\n        }\n\n        return result.minus(result.times(taxRate));\n    }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>이처럼 중복코드는 새로운 중복코드를 부른다.</li>\n<li><code>minus</code>를 호출하고 있다.</li>\n</ul>\n<h4 id=\"-타입-코드-사용하기\" style=\"position:relative;\"><a href=\"#-%ED%83%80%EC%9E%85-%EC%BD%94%EB%93%9C-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0\" aria-label=\" 타입 코드 사용하기 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🎈 타입 코드 사용하기</h4>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">@Getter\n@RequiredArgsConstructor\npublic class Phone {\n\n    private static final int LATE_NIGHT_HOUR = 22;\n\n    enum PhoneType {REGULAR, NIGHTLY}\n\n    private final PhoneType type;\n\n    private final Money amount;\n\n    private final Money regularAmount;\n\n    private final Money nightlyAmount;\n\n    private final Duration seconds;\n\n    private List&lt;Call&gt; calls = new ArrayList&lt;&gt;();\n\n    public Phone(Money amount, Duration seconds) {\n        this(PhoneType.REGULAR, amount, Money.ZERO, Money.ZERO, seconds);\n    }\n\n    public Phone(Money regularAmount, Money nightlyAmount, Duration seconds) {\n        this(PhoneType.NIGHTLY, Money.ZERO, regularAmount, nightlyAmount, seconds);\n    }\n\n    public Money calculateFee() {\n        Money result = Money.ZERO;\n\n        for (Call call : calls) {\n            if (type == PhoneType.REGULAR) {\n                result = result.plus(amount.times((double) call.getDuration().getSeconds() / seconds.getSeconds()));\n            } else {\n                if (call.getFrom().getHour() &gt;= LATE_NIGHT_HOUR) {\n                    result = result.plus(nightlyAmount.times((double) call.getDuration().getSeconds() / seconds.getSeconds()));\n                } else {\n                    result = result.plus(regularAmount.times((double) call.getDuration().getSeconds() / seconds.getSeconds()));\n                }\n            }\n        }\n\n        return result;\n    }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>낮은 응집도와 높은 결합도를 가진다.</li>\n</ul>\n<h3 id=\"-1013-상속을-이용해서-중복-코드-제거하기\" style=\"position:relative;\"><a href=\"#-1013-%EC%83%81%EC%86%8D%EC%9D%84-%EC%9D%B4%EC%9A%A9%ED%95%B4%EC%84%9C-%EC%A4%91%EB%B3%B5-%EC%BD%94%EB%93%9C-%EC%A0%9C%EA%B1%B0%ED%95%98%EA%B8%B0\" aria-label=\" 1013 상속을 이용해서 중복 코드 제거하기 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 10.1.3 상속을 이용해서 중복 코드 제거하기</h3>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">public class NightlyDiscountPhone extends Phone {\n    private static final int LATE_NIGHT_HOUR = 22;\n\n    private Money nightlyAmount;\n\n    public NightlyDiscountPhone(Money nightlyAmount, Money regularAmount, Duration seconds) {\n        super(regularAmount, seconds);\n        this.nightlyAmount = nightlyAmount;\n    }\n\n    @Override\n    public Money calculateFee() {\n        Money result = super.calculateFee();\n        \n        Money nightlyFee = Money.ZERO;\n        for (Call call : getCalls()) {\n            if (call.getFrom().getHour() &gt;= LATE_NIGHT_HOUR) {\n                nightlyFee = nightlyFee.plus(getAmount().minus(nightlyAmount.times((double) call.getDuration().getSeconds() / getSeconds().getSeconds())));\n            }\n        }\n        \n        return result.minus(nightlyFee);\n    }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>위 코드 처럼 상속을 염두에 두고 설계되지 않은 클래스를 상속을 이용해 재사용하는 것은 쉽지 않다.</li>\n<li>자식 클래스의 작성자가 부모 클래스의 구현 방법에 대한 정확한 지식을 가져야 한다.</li>\n</ul>\n<h3 id=\"-1014-강하게-결합된-phone과-nightlydiscountphone\" style=\"position:relative;\"><a href=\"#-1014-%EA%B0%95%ED%95%98%EA%B2%8C-%EA%B2%B0%ED%95%A9%EB%90%9C-phone%EA%B3%BC-nightlydiscountphone\" aria-label=\" 1014 강하게 결합된 phone과 nightlydiscountphone permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 10.1.4 강하게 결합된 Phone과 NightlyDiscountPhone</h3>\n<p>새로운 요구사항: 세금 부과</p>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">@Getter\n@RequiredArgsConstructor\npublic class Phone {\n\n    private final Money amount;\n\n    private final Duration seconds;\n\n    private final double taxRate;\n\n    private List&lt;Call&gt; calls = new ArrayList&lt;&gt;();\n\n    public Money calculateFee() {\n        Money result = Money.ZERO;\n\n        for (Call call : calls) {\n            result = result.plus(amount.times((double) call.getDuration().getSeconds() / seconds.getSeconds()));\n        }\n\n        return result.plus(result.times(taxRate));\n    }\n}</code>\n        </deckgo-highlight-code>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">public class NightlyDiscountPhone extends Phone {\n    private static final int LATE_NIGHT_HOUR = 22;\n\n    private Money nightlyAmount;\n\n    public NightlyDiscountPhone(Money nightlyAmount, Money regularAmount, Duration seconds, double taxRate) {\n        super(regularAmount, seconds, taxRate);\n        this.nightlyAmount = nightlyAmount;\n    }\n\n    @Override\n    public Money calculateFee() {\n        Money result = super.calculateFee();\n\n        Money nightlyFee = Money.ZERO;\n        for (Call call : getCalls()) {\n            if (call.getFrom().getHour() &gt;= LATE_NIGHT_HOUR) {\n                nightlyFee = nightlyFee.plus(getAmount().minus(nightlyAmount.times((double) call.getDuration().getSeconds() / getSeconds().getSeconds())));\n            }\n        }\n\n        return result.minus(nightlyFee.plus(nightlyFee.times(getTaxRate())));\n    }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>중복 로직을 제거하기 위해 상속을 사용했음에도 중복 코드가 생겼다❗</li>\n</ul>\n<blockquote>\n<p>자식 클래스의 메서드 안에서 super 참조를 이용해 부모 클래스의 메서드를 직접 호출할 경우 두 클래스는 강하게 결합된다. super 호출을 제거할 수 있는 방법을 찾아 결합도를 제거하라.</p>\n</blockquote>\n<h2 id=\"-102-취약한-기반-클래스-문제\" style=\"position:relative;\"><a href=\"#-102-%EC%B7%A8%EC%95%BD%ED%95%9C-%EA%B8%B0%EB%B0%98-%ED%81%B4%EB%9E%98%EC%8A%A4-%EB%AC%B8%EC%A0%9C\" aria-label=\" 102 취약한 기반 클래스 문제 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>📖 10.2 취약한 기반 클래스 문제</h2>\n<ul>\n<li>부모 클래스의 변경에 의해 자식 클래스가 영향을 받는 현상</li>\n<li>상속을 사용한다면 피할 수 없는 OOP의 근본적인 취약성</li>\n<li>캡슐화를 약화시키고 결합도를 높인다.</li>\n</ul>\n<h3 id=\"-1021-불필요한-인터페이스-상속-문제\" style=\"position:relative;\"><a href=\"#-1021-%EB%B6%88%ED%95%84%EC%9A%94%ED%95%9C-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4-%EC%83%81%EC%86%8D-%EB%AC%B8%EC%A0%9C\" aria-label=\" 1021 불필요한 인터페이스 상속 문제 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 10.2.1 불필요한 인터페이스 상속 문제</h3>\n<p>Java 초기 버전의 대표적인 사례</p>\n<ul>\n<li><code>java.util.Stack</code>\n<ul>\n<li>Stack을 Vector의 자식 클래스로 구현</li>\n<li>Stack이 규칙을 무너뜨릴 여지가 있는 위험한 Vector의 퍼블릭 인터페이스까지도 함께 상속받음.</li>\n</ul>\n</li>\n<li><code>java.util.Properties</code>\n<ul>\n<li>Map의 조상인 Hashtable을 상속</li>\n<li>String 타입 이외의 키와 값이라도 저장이 가능하게 되어버림.</li>\n</ul>\n</li>\n</ul>\n<blockquote>\n<p>상속받은 부모 클래스의 메서드가 자식 클래스의 내부 구조에 대한 규칙을 깨트릴 수 있다.</p>\n</blockquote>\n<h3 id=\"-1022-메서드-오버라이딩의-오작용-문제\" style=\"position:relative;\"><a href=\"#-1022-%EB%A9%94%EC%84%9C%EB%93%9C-%EC%98%A4%EB%B2%84%EB%9D%BC%EC%9D%B4%EB%94%A9%EC%9D%98-%EC%98%A4%EC%9E%91%EC%9A%A9-%EB%AC%B8%EC%A0%9C\" aria-label=\" 1022 메서드 오버라이딩의 오작용 문제 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 10.2.2 메서드 오버라이딩의 오작용 문제</h3>\n<ul>\n<li><a href=\"https://bottleh.netlify.app/backend/effective-java/4%EC%9E%A5_%ED%81%B4%EB%9E%98%EC%8A%A4%EC%99%80_%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4/#%EC%95%84%EC%9D%B4%ED%85%9C18-%EC%83%81%EC%86%8D%EB%B3%B4%EB%8B%A4%EB%8A%94-%EC%BB%B4%ED%8F%AC%EC%A7%80%EC%85%98%EC%9D%84-%EC%82%AC%EC%9A%A9%ED%95%98%EB%9D%BC\">이펙티브 자바 아이템18</a></li>\n</ul>\n<blockquote>\n<p>자식 클래스가 부모 클래스의 메서드를 오버라이딩할 경우 부모 클래스가 자식의 메서드를 사용하는 방법에 자식 클래스가 결합될 수 있다.</p>\n</blockquote>\n<h3 id=\"-1023-부모-클래스와-자식-클래스의-동시-수정-문제\" style=\"position:relative;\"><a href=\"#-1023-%EB%B6%80%EB%AA%A8-%ED%81%B4%EB%9E%98%EC%8A%A4%EC%99%80-%EC%9E%90%EC%8B%9D-%ED%81%B4%EB%9E%98%EC%8A%A4%EC%9D%98-%EB%8F%99%EC%8B%9C-%EC%88%98%EC%A0%95-%EB%AC%B8%EC%A0%9C\" aria-label=\" 1023 부모 클래스와 자식 클래스의 동시 수정 문제 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 10.2.3 부모 클래스와 자식 클래스의 동시 수정 문제</h3>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">@Getter\n@RequiredArgsConstructor\npublic class Song {\n\n    private final String singer;\n    \n    private final String title;\n}</code>\n        </deckgo-highlight-code>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">public class Playlist {\n\n    @Getter\n    private final List&lt;Song&gt; tracks = new ArrayList&lt;&gt;();\n\n    public void append(Song song) {\n        getTracks().add(song);\n    }\n}</code>\n        </deckgo-highlight-code>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">public class PersonalPlaylist extends Playlist {\n    \n    public void remove(Song song) {\n        getTracks().remove(song);\n    }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>새로운 요구사항: 가수별 노래의 제목도 함께 관리</li>\n</ul>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">@Getter\npublic class Playlist {\n\n    private final List&lt;Song&gt; tracks = new ArrayList&lt;&gt;();\n\n    private final Map&lt;String, String&gt; singers = new HashMap&lt;&gt;();\n\n    public void append(Song song) {\n        getTracks().add(song);\n        singers.put(song.getSinger(), song.getTitle());\n    }\n}</code>\n        </deckgo-highlight-code>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">public class PersonalPlaylist extends Playlist {\n\n    public void remove(Song song) {\n        getTracks().remove(song);\n        getSingers().remove(song.getSinger());\n    }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>결합도란 다른 대상에 대해 알고 있는 지식의 양이다.</li>\n</ul>\n<blockquote>\n<p>클래스를 상속하면 결합도로 인해 자식 클래스와 부모 클래스의 구현을 영원히 변경하지 않거나, 자식 클래스와 부모 클래스를 동시에 변경하거나 둘 중 하나를 선택할 수 밖에 없다.</p>\n</blockquote>\n<h2 id=\"-103-phone-다시-살펴보기\" style=\"position:relative;\"><a href=\"#-103-phone-%EB%8B%A4%EC%8B%9C-%EC%82%B4%ED%8E%B4%EB%B3%B4%EA%B8%B0\" aria-label=\" 103 phone 다시 살펴보기 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>📖 10.3 Phone 다시 살펴보기</h2>\n<h3 id=\"-1031-추상화에-의존하자\" style=\"position:relative;\"><a href=\"#-1031-%EC%B6%94%EC%83%81%ED%99%94%EC%97%90-%EC%9D%98%EC%A1%B4%ED%95%98%EC%9E%90\" aria-label=\" 1031 추상화에 의존하자 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 10.3.1 추상화에 의존하자</h3>\n<ul>\n<li>부모 클래스와 자식 클래스 모두 추상화에 의존하도록 수정</li>\n</ul>\n<p>코드 중복을 제거하기 위해 상속을 도입할 때 따르는 원칙</p>\n<ol>\n<li>두 메서드가 유사하게 보인다면 차이점을 메서드로 추출</li>\n<li>부모 클래스의 코드를 하위로 내리지 말고 자식 클래스의 코드를 상위로 옮겨라.</li>\n</ol>\n<h3 id=\"-1032-차이를-메서드로-추출하라\" style=\"position:relative;\"><a href=\"#-1032-%EC%B0%A8%EC%9D%B4%EB%A5%BC-%EB%A9%94%EC%84%9C%EB%93%9C%EB%A1%9C-%EC%B6%94%EC%B6%9C%ED%95%98%EB%9D%BC\" aria-label=\" 1032 차이를 메서드로 추출하라 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 10.3.2 차이를 메서드로 추출하라</h3>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">@Getter\n@RequiredArgsConstructor\npublic class Phone {\n\n    private final Money amount;\n\n    private final Duration seconds;\n\n    private List&lt;Call&gt; calls = new ArrayList&lt;&gt;();\n\n    public Money calculateFee() {\n        Money result = Money.ZERO;\n\n        for (Call call : calls) {\n            result = result.plus(calculateCallFee(call));\n        }\n\n        return result;\n    }\n\n    private Money calculateCallFee(Call call) {\n        return amount.times((double) call.getDuration().getSeconds() / seconds.getSeconds());\n    }\n}</code>\n        </deckgo-highlight-code>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">@RequiredArgsConstructor\npublic class NightlyDiscountPhone {\n\n    private static final int LATE_NIGHT_HOUR = 22;\n\n    private final Money nightlyAmount;\n\n    private final Money regularAmount;\n\n    private final Duration seconds;\n\n    private List&lt;Call&gt; calls = new ArrayList&lt;&gt;();\n\n    public Money calculateCallFee() {\n        Money result = Money.ZERO;\n\n        for (Call call : calls) {\n            result = result.plus(calculateCallFee(call));\n        }\n\n        return result;\n    }\n\n    private Money calculateCallFee(Call call) {\n        if (call.getFrom().getHour() &gt;= LATE_NIGHT_HOUR) {\n            return nightlyAmount.times((double) call.getDuration().getSeconds() / seconds.getSeconds());\n        }\n        return regularAmount.times((double) call.getDuration().getSeconds() / seconds.getSeconds());\n    }\n}</code>\n        </deckgo-highlight-code>\n<h3 id=\"-1033-중복-코드를-부모-클래스로-올려라\" style=\"position:relative;\"><a href=\"#-1033-%EC%A4%91%EB%B3%B5-%EC%BD%94%EB%93%9C%EB%A5%BC-%EB%B6%80%EB%AA%A8-%ED%81%B4%EB%9E%98%EC%8A%A4%EB%A1%9C-%EC%98%AC%EB%A0%A4%EB%9D%BC\" aria-label=\" 1033 중복 코드를 부모 클래스로 올려라 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 10.3.3 중복 코드를 부모 클래스로 올려라</h3>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">public abstract class AbstractPhone {\n\n    private final List&lt;Call&gt; calls = new ArrayList&lt;&gt;();\n\n    public Money calculateFee() {\n        Money result = Money.ZERO;\n\n        for (Call call : calls) {\n            result = result.plus(calculateCallFee(call));\n        }\n\n        return result;\n    }\n\n    protected abstract Money calculateCallFee(Call call);\n}</code>\n        </deckgo-highlight-code>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">@Getter\n@RequiredArgsConstructor\npublic class Phone extends AbstractPhone{\n\n    private final Money amount;\n\n    private final Duration seconds;\n\n    @Override\n    protected Money calculateCallFee(Call call) {\n        return amount.times((double) call.getDuration().getSeconds() / seconds.getSeconds());\n    }\n}</code>\n        </deckgo-highlight-code>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">@RequiredArgsConstructor\npublic class NightlyDiscountPhone extends AbstractPhone {\n\n    private static final int LATE_NIGHT_HOUR = 22;\n\n    private final Money nightlyAmount;\n\n    private final Money regularAmount;\n\n    private final Duration seconds;\n\n    @Override\n    protected Money calculateCallFee(Call call) {\n        if (call.getFrom().getHour() &gt;= LATE_NIGHT_HOUR) {\n            return nightlyAmount.times((double) call.getDuration().getSeconds() / seconds.getSeconds());\n        }\n        return regularAmount.times((double) call.getDuration().getSeconds() / seconds.getSeconds());\n    }\n}</code>\n        </deckgo-highlight-code>\n<h3 id=\"-1034-추상화가-핵심이다\" style=\"position:relative;\"><a href=\"#-1034-%EC%B6%94%EC%83%81%ED%99%94%EA%B0%80-%ED%95%B5%EC%8B%AC%EC%9D%B4%EB%8B%A4\" aria-label=\" 1034 추상화가 핵심이다 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 10.3.4 추상화가 핵심이다</h3>\n<ul>\n<li>세 클래스는 각각 하나의 변경 이유만을 가진다.\n<ul>\n<li>단일 책임 원칙을 준수하므로 응집도가 높다.</li>\n</ul>\n</li>\n<li>부모 클래스의 내부 구현이 변경되더라도 자식 클래스는 영향을 받지 않는다.</li>\n<li>상속 계층이 코드를 진화시키는 데 걸림돌이 된다면 추상화를 찾아내고 상속 계층 안의 클래스들이 그 추상화에 의존하도록 코드를 리팩터링</li>\n</ul>\n<h3 id=\"-1035-의도를-드러내는-이름-선택하기\" style=\"position:relative;\"><a href=\"#-1035-%EC%9D%98%EB%8F%84%EB%A5%BC-%EB%93%9C%EB%9F%AC%EB%82%B4%EB%8A%94-%EC%9D%B4%EB%A6%84-%EC%84%A0%ED%83%9D%ED%95%98%EA%B8%B0\" aria-label=\" 1035 의도를 드러내는 이름 선택하기 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 10.3.5 의도를 드러내는 이름 선택하기</h3>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">public abstract class Phone {\n}\n\npublic class RegularPhone extends Phone {\n}\n\npublic class NightlyDiscountPhone extends Phone {\n}</code>\n        </deckgo-highlight-code>\n<h3 id=\"-1036-세금-추가하기\" style=\"position:relative;\"><a href=\"#-1036-%EC%84%B8%EA%B8%88-%EC%B6%94%EA%B0%80%ED%95%98%EA%B8%B0\" aria-label=\" 1036 세금 추가하기 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 10.3.6 세금 추가하기</h3>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">@RequiredArgsConstructor\npublic abstract class Phone {\n    \n    private final double taxRate;\n    \n    private List&lt;Call&gt; calls = new ArrayList&lt;&gt;();\n\n    public Money calculateFee() {\n        Money result = Money.ZERO;\n\n        for (Call call : calls) {\n            result = result.plus(calculateCallFee(call));\n        }\n\n        return result.plus(result.times(taxRate));\n    }\n\n    protected abstract Money calculateCallFee(Call call);\n}</code>\n        </deckgo-highlight-code>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">@Getter\npublic class RegularPhone extends Phone {\n\n    private final Money amount;\n\n    private final Duration seconds;\n\n    public RegularPhone(double taxRate, Money amount, Duration seconds) {\n        super(taxRate);\n        this.amount = amount;\n        this.seconds = seconds;\n    }\n\n    @Override\n    protected Money calculateCallFee(Call call) {\n        return amount.times((double) call.getDuration().getSeconds() / seconds.getSeconds());\n    }\n}</code>\n        </deckgo-highlight-code>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">public class NightlyDiscountPhone extends Phone {\n\n    private static final int LATE_NIGHT_HOUR = 22;\n\n    private final Money nightlyAmount;\n\n    private final Money regularAmount;\n\n    private final Duration seconds;\n\n    public NightlyDiscountPhone(double taxRate, Money nightlyAmount, Money regularAmount, Duration seconds) {\n        super(taxRate);\n        this.nightlyAmount = nightlyAmount;\n        this.regularAmount = regularAmount;\n        this.seconds = seconds;\n    }\n\n    @Override\n    protected Money calculateCallFee(Call call) {\n        if (call.getFrom().getHour() &gt;= LATE_NIGHT_HOUR) {\n            return nightlyAmount.times((double) call.getDuration().getSeconds() / seconds.getSeconds());\n        }\n        return regularAmount.times((double) call.getDuration().getSeconds() / seconds.getSeconds());\n    }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>책임을 아무리 잘 분리하더라도 인스턴스 변수의 추가는 종종 상속 계층 전반에 걸친 변경을 유발</li>\n<li>우리가 원하는 것은 행동을 변경하기 위해 인스턴스 변수를 추가하더라도 상속 계층 전체에 걸쳐 부작용이 퍼지지 않게 막는 것</li>\n</ul>\n<h2 id=\"-104-차이에-의한-프로그래밍\" style=\"position:relative;\"><a href=\"#-104-%EC%B0%A8%EC%9D%B4%EC%97%90-%EC%9D%98%ED%95%9C-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D\" aria-label=\" 104 차이에 의한 프로그래밍 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>📖 10.4 차이에 의한 프로그래밍</h2>\n<ul>\n<li>기존 코드와 다른 부분만을 추가함으로써 애플리케이션의 기능을 확장하는 방법</li>\n<li>중복 코드를 제거하고 코드를 재사용하는 것이 목표</li>\n<li>상속은 코드 재사용과 관련된 대부분의 경우에 우아한 해결 방법이 아니다.\n<ul>\n<li><strong>합성</strong>이 더 좋은 방법❗</li>\n</ul>\n</li>\n</ul>","excerpt":"코드를 재사용하려는 강력한 동기 이면에는 중복된 코드를 제거하려는 욕망이 숨어 있다. 📖 10.1 상속과 중복 코드 중복 코드는 사람들의 마음속에 의심과 불신의 씨앗을 뿌린다. 🔖 10.1.1 DRY 원칙 중복 코드는 변경을 방해한다. 이것이 중복 코드를 제거해야 하는 가장 큰 이유다. 중복 여부를 판단하는 기준은 변경이다. 요구사항이 변경됐을 때 두 코드를 함께 수정해야 한다면 중복 DRY 원칙을 따르자. Don't Repeat Yourself…","fields":{"slug":"/backend/object/chapter10/상속과_코드_재사용/"},"frontmatter":{"title":"Object - 10장 상속과 코드 재사용","thumbnail":{"childImageSharp":{"fluid":{"src":"/static/dd0e1990925e942697544e2bdcd9332e/9b73b/object.png"}}},"draft":false,"category":"Back-End","tags":["java"],"date":"October 29, 2023"}}}},"staticQueryHashes":["2374173507","2996537568","3691437124"]}