{"componentChunkName":"component---src-containers-post-index-tsx","path":"/backend/object/chapter6/메시지와_인터페이스/","result":{"pageContext":{"next":{"id":"7ab712a5-7cde-5b5e-bbc7-daa941ff6de3","html":"<p>책임 할당 과정은 일종의 trade-off 활동이다.</p>\n<h2 id=\"-51-책임-주도-설계를-향해\" style=\"position:relative;\"><a href=\"#-51-%EC%B1%85%EC%9E%84-%EC%A3%BC%EB%8F%84-%EC%84%A4%EA%B3%84%EB%A5%BC-%ED%96%A5%ED%95%B4\" aria-label=\" 51 책임 주도 설계를 향해 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>📖 5.1 책임 주도 설계를 향해</h2>\n<ul>\n<li>데이터보다 행동을 먼저 결정하라</li>\n<li>협력이라는 문맥 안에서 책임을 결정하라</li>\n</ul>\n<h3 id=\"-511-데이터보다-행동을-먼저-결정하라\" style=\"position:relative;\"><a href=\"#-511-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B3%B4%EB%8B%A4-%ED%96%89%EB%8F%99%EC%9D%84-%EB%A8%BC%EC%A0%80-%EA%B2%B0%EC%A0%95%ED%95%98%EB%9D%BC\" aria-label=\" 511 데이터보다 행동을 먼저 결정하라 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>🔖 5.1.1 데이터보다 행동을 먼저 결정하라</h3>\n<ul>\n<li>객체에게 중요한 것은 데이터가 아니라 외부에 제공하는 행동\n<ul>\n<li>객체가 수행하는 행동이란 곧 책임을 의미</li>\n</ul>\n</li>\n<li>데이터는 객체가 책임을 수행하는 데 필요한 재료를 제공\n<ul>\n<li>너무 이른 시기에 데이터에 초점을 맞추면 객체의 캡슐화가 약화</li>\n</ul>\n</li>\n<li>책임 중심 설계의 순서\n<ul>\n<li>첫 번째로 <code>이 객체가 수행해야 하는 책임은 무엇인가</code>를 결정</li>\n<li>두 번째로 <code>이 책임을 수행하는 데 필요한 데이터는 무엇인가</code>를 결정</li>\n<li>책임을 먼저 결정한 후에 객체의 상태를 결정해야 한다.</li>\n</ul>\n</li>\n</ul>\n<h3 id=\"-512-협력이라는-문맥-안에서-책임을-결정하라\" style=\"position:relative;\"><a href=\"#-512-%ED%98%91%EB%A0%A5%EC%9D%B4%EB%9D%BC%EB%8A%94-%EB%AC%B8%EB%A7%A5-%EC%95%88%EC%97%90%EC%84%9C-%EC%B1%85%EC%9E%84%EC%9D%84-%EA%B2%B0%EC%A0%95%ED%95%98%EB%9D%BC\" aria-label=\" 512 협력이라는 문맥 안에서 책임을 결정하라 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>🔖 5.1.2 협력이라는 문맥 안에서 책임을 결정하라</h3>\n<ul>\n<li>객체에게 할당된 책임의 품질은 협력에 적합한 정도로 결정</li>\n<li>책임은 객체의 입장이 아니라 객체가 참여하는 협력에 적합</li>\n<li>협력에 적합한 책임이란 메시지 수신자가 아니라 메시지 전송자에게 적합한 책임을 의미\n<ul>\n<li>협력을 시작한 주체는 메시지 전송자이기 때문</li>\n</ul>\n</li>\n<li>메시지를 결정한 후에 객체를 선택해야 한다.</li>\n</ul>\n<blockquote>\n<p>\"메시지를 전송해야 하는데 누구에게 전송해야 하지?\"라고 질문하는 것이 메시지 기반 설계로 향하는 첫걸음이다.</p>\n<p>객체를 가지고 있기 때문에 메시지를 보내는 것이 아니다. 메시지를 전송하기 때문에 객체를 갖게 된 것이다.</p>\n</blockquote>\n<p>결론적으로 책임 중심의 설계에서는 협력이라는 문맥 안에서 객체가 수행할 책임에 초점을 맞춘다.</p>\n<h3 id=\"-513-책임-주도-설계\" style=\"position:relative;\"><a href=\"#-513-%EC%B1%85%EC%9E%84-%EC%A3%BC%EB%8F%84-%EC%84%A4%EA%B3%84\" aria-label=\" 513 책임 주도 설계 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>🔖 5.1.3 책임 주도 설계</h3>\n<ul>\n<li>시스템이 사용자에게 제공해야 하는 기능인 시스템 책임을 파악</li>\n<li>시스템 책임을 더 작은 책임으로 분할</li>\n<li>분할된 책임을 수행할 수 있는 적절한 객체 또는 역할을 찾아 책임을 할당</li>\n<li>객체가 책임을 수행하는 도중 다른 객체의 도움이 필요한 경우 이를 책임질 적절한 객체 또는 역할을 찾는다.</li>\n<li>해당 객체 또는 역할에게 책임을 할당함으로써 두 객체가 협력하게 한다.</li>\n</ul>\n<p>책임 주도 설계의 핵심은 책임을 결정한 후에 책임을 수행할 객체를 결정하는 것이다. 그리고 협력에 참여하는 객체들의 책임이 어느 정도 정리될 때까지는 객체의 내부 상태에 대해 관심을 가지지 않는 것이다.</p>\n<h2 id=\"-52-책임-할당을-위한-grasp-패턴\" style=\"position:relative;\"><a href=\"#-52-%EC%B1%85%EC%9E%84-%ED%95%A0%EB%8B%B9%EC%9D%84-%EC%9C%84%ED%95%9C-grasp-%ED%8C%A8%ED%84%B4\" aria-label=\" 52 책임 할당을 위한 grasp 패턴 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>📖 5.2 책임 할당을 위한 GRASP 패턴</h2>\n<p>General Responsibility Assignment Software Pattern</p>\n<ul>\n<li>객체에게 책임을 할당할 때 지침으로 삼을 수 있는 원칙들의 집합을 패턴 형식으로 정리</li>\n</ul>\n<h3 id=\"-521-도메인-개념에서-출발하기\" style=\"position:relative;\"><a href=\"#-521-%EB%8F%84%EB%A9%94%EC%9D%B8-%EA%B0%9C%EB%85%90%EC%97%90%EC%84%9C-%EC%B6%9C%EB%B0%9C%ED%95%98%EA%B8%B0\" aria-label=\" 521 도메인 개념에서 출발하기 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>🔖 5.2.1 도메인 개념에서 출발하기</h3>\n<ul>\n<li>어떤 책임을 할당해야 할 때 가장 먼저 고민해야 하는 유력한 후보는 바로 도메인 개념이다.</li>\n<li>올바른 도메인 모델이란 존재하지 않는다\n<ul>\n<li>실용적이면서 유용한 모델이 답이다. 즉, 구현에 도움이 되는 모델</li>\n</ul>\n</li>\n</ul>\n<h3 id=\"-522-정보-전문가에게-책임을-할당해라\" style=\"position:relative;\"><a href=\"#-522-%EC%A0%95%EB%B3%B4-%EC%A0%84%EB%AC%B8%EA%B0%80%EC%97%90%EA%B2%8C-%EC%B1%85%EC%9E%84%EC%9D%84-%ED%95%A0%EB%8B%B9%ED%95%B4%EB%9D%BC\" aria-label=\" 522 정보 전문가에게 책임을 할당해라 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>🔖 5.2.2 정보 전문가에게 책임을 할당해라</h3>\n<p>information expert pattern</p>\n<ul>\n<li>책임을 수행할 정보를 알고 있는 객체에게 책임을 할당하는 것</li>\n<li>정보와 행동을 최대한 가까운 곳에 위치시키기 때문에 캡슐화 유지 가능</li>\n<li>객체란 상태와 행동을 함께 가지는 단위라는 객체지향의 가장 기본적인 원리를 책임 할당의 관점에서 표현</li>\n</ul>\n<h3 id=\"-523-높은-응집도와-낮은-결합도\" style=\"position:relative;\"><a href=\"#-523-%EB%86%92%EC%9D%80-%EC%9D%91%EC%A7%91%EB%8F%84%EC%99%80-%EB%82%AE%EC%9D%80-%EA%B2%B0%ED%95%A9%EB%8F%84\" aria-label=\" 523 높은 응집도와 낮은 결합도 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>🔖 5.2.3 높은 응집도와 낮은 결합도</h3>\n<p><code>Movie</code> 대신 <code>Screening</code>이 직접 <code>DiscountCondition</code>과 협력하게 하는 것은 어떨까?</p>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1191px; \"\n    >\n      <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 50.66666666666667%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAAAsTAAALEwEAmpwYAAABH0lEQVQoz41SiaqEMAzs/3+aIPgBKl54X1vP1iPL5D1FF5f3AqEhSSeTacW+73T1wz7zR63ve2qa5rEOF7cE/Zxa6xv41eI4pjAMaZqmY/StLp4YOI5DwzBwbtu205dloaIoyPd9ZnmtrevKp7iCKaWormvKsozSNOXLALcsi5IkoTzPmSFyURRxr23b5LoutW1LXdd9rHxZE9PAEo1gA+ZgWFUVg8/zzHWAwI+6+CbuAY5V0AiDtgAvy5JBnnT+ExAygA0MJ9b2PI8lAUNIgBiPhF7x7TWvBqBxHPkS9A2CgIGhoWEYZJomS4MBDDgvCymtTzb6N4YDaBwGjmFYF6zQg9rRi/j8h51S1I8jydeLpJSn0J2U1LYNPwSm/8fe0UMKdPQyFtMAAAAASUVORK5CYII='); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"5-2\"\n        title=\"5-2\"\n        src=\"/static/bcafdfbaee48fccc641c611139d669b7/19a6b/5-2.png\"\n        srcset=\"/static/bcafdfbaee48fccc641c611139d669b7/5a46d/5-2.png 300w,\n/static/bcafdfbaee48fccc641c611139d669b7/0a47e/5-2.png 600w,\n/static/bcafdfbaee48fccc641c611139d669b7/19a6b/5-2.png 1191w\"\n        sizes=\"(max-width: 1191px) 100vw, 1191px\"\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>응집도와 결합도는 다르다!</li>\n<li>low coupling pattern의 관점에서는 <code>Movie</code>가 <code>DiscountCondition</code>과 협력하는 것이 더 나은 설계 대안</li>\n<li>high cohesion pattern의 관점에서는 <code>Movie</code>가 <code>DiscountCondition</code>과 협력하는 것이 더 나은 설계 대안</li>\n</ul>\n<h3 id=\"-524-창조자에게-객체-생성-책임을-할당하라\" style=\"position:relative;\"><a href=\"#-524-%EC%B0%BD%EC%A1%B0%EC%9E%90%EC%97%90%EA%B2%8C-%EA%B0%9D%EC%B2%B4-%EC%83%9D%EC%84%B1-%EC%B1%85%EC%9E%84%EC%9D%84-%ED%95%A0%EB%8B%B9%ED%95%98%EB%9D%BC\" aria-label=\" 524 창조자에게 객체 생성 책임을 할당하라 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>🔖 5.2.4 창조자에게 객체 생성 책임을 할당하라</h3>\n<p>Creator pattern</p>\n<ul>\n<li>객체 A를 생성해야 할 때 아래 조건을 최대한 많이 만족하는 B에게 객체 생성 책임을 할당하라\n<ul>\n<li>B가 A 객체를 포함하거나 참조한다.</li>\n<li>B가 A 객체를 기록한다.</li>\n<li>B가 A 객체를 긴밀하게 사용한다.</li>\n<li>B가 A 객체를 초기화하는 데 필요한 데이터를 가지고 있다.(이 경우 B는 A에 대한 정보 전문가다.)</li>\n</ul>\n</li>\n</ul>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1200px; \"\n    >\n      <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 35.333333333333336%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAYAAAAIy204AAAACXBIWXMAAAsTAAALEwEAmpwYAAAA+klEQVQoz12RWaqGMAyF3f+aXIeI+qA44FSt8+z5OeFW9AZCQ5Kefkmt+75B27YNZVniOA4wZ/I8z/NE0zSoqgrLsuC6LsnzpJs+umWCvu8RRRGUUnJJay3xNI2YpglFUSAMQ7Rti33fnz46HzSiFgMm1nUVymEYpJninuchz3PJscYeCtJ935eJSM4HPoT/jWNQIEmSR+Ddx5jjG0Fz/hHen52Rdp5ndF2HNE2FjmSmPo6jUJMwyzKJTf2zw/fnUMh1XTiOI5TckyHjboMggG3biONYYgI8O+zXDc0wyFi6baFUDVXXMhJzjVKyI2P8INJLv9ZCfL+m/AEFGBwBPjhyQgAAAABJRU5ErkJggg=='); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"5-3\"\n        title=\"5-3\"\n        src=\"/static/54baf4d47f5bb08067f0e7e5fc7ece36/c1b63/5-3.png\"\n        srcset=\"/static/54baf4d47f5bb08067f0e7e5fc7ece36/5a46d/5-3.png 300w,\n/static/54baf4d47f5bb08067f0e7e5fc7ece36/0a47e/5-3.png 600w,\n/static/54baf4d47f5bb08067f0e7e5fc7ece36/c1b63/5-3.png 1200w,\n/static/54baf4d47f5bb08067f0e7e5fc7ece36/e9140/5-3.png 1226w\"\n        sizes=\"(max-width: 1200px) 100vw, 1200px\"\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>Screening</code>을 <code>Reservation</code>의 Creator로 선택</li>\n</ul>\n<h2 id=\"-53-구현을-통한-검증\" style=\"position:relative;\"><a href=\"#-53-%EA%B5%AC%ED%98%84%EC%9D%84-%ED%86%B5%ED%95%9C-%EA%B2%80%EC%A6%9D\" aria-label=\" 53 구현을 통한 검증 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>📖 5.3 구현을 통한 검증</h2>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">/**\n * 상영 Class\n */\npublic class Screening {\n\n    /**\n     * 영화\n     */\n    private Movie movie;\n\n    /**\n     * 순번\n     */\n    @Getter\n    private int sequence;\n\n    /**\n     * 상영 시작 시간\n     */\n    @Getter\n    private LocalDateTime whenScreened;\n\n    public Reservation reserve(Customer customer, int audienceCount) {\n        return new Reservation(customer, this, calculateFee(audienceCount), audienceCount);\n    }\n    \n    private Money calculateFee(int audienceCount) {\n        return movie.calculateMovieFee(this).times(audienceCount);\n    }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li><code>Movie</code>에 전송하는 메시지의 시그니처를 <code>calculateMovieFee</code>으로 선언</li>\n<li>메시지가 변경되지 않는 한 <code>Movie</code>에 어떤 수정을 가하더라도 <code>Screening</code>에는 영향을 미치지 않는다.</li>\n</ul>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">public class Movie {\n\n    /**\n     * 제목\n     */\n    private String title;\n\n    /**\n     * 상영 시간\n     */\n    private Duration runningTime;\n\n    /**\n     * 기본 요금\n     */\n    private Money fee;\n\n    /**\n     * 할인 조건 목록\n     */\n    private List&lt;DiscountCondition&gt; discountConditions;\n\n    /**\n     * 할인 정책의 종류를 결정하는 변수\n     */\n    private MovieType movieType;\n\n    /**\n     * 할인 금액\n     */\n    private Money discountAmount;\n\n    /**\n     * 할인 비율\n     */\n    private double discountPercent;\n\n    public Money calculateMovieFee(Screening screening) {\n        if (isDiscountable(screening)) {\n            return fee.minus(calculateDiscountAmount());\n        }\n        \n        return fee;\n    }\n    \n    private boolean isDiscountable(Screening screening) {\n        return discountConditions.stream().anyMatch(condition -&gt; condition.isSatisfiedBy(screening));\n    }\n    \n    private Money calculateDiscountAmount() {\n        switch (movieType) {\n            case AMOUNT_DISCOUNT -&gt; {\n                return calculateAmountDiscountAmount();\n            }\n            case PERCENT_DISCOUNT -&gt; {\n                return calculatePercentDiscountAmount();\n            }\n            case NONE_DISCOUNT -&gt; {\n                return calculateNoneDiscountAmount();\n            }\n        }\n        \n        throw new IllegalArgumentException();\n    }\n    \n    private Money calculateAmountDiscountAmount() {\n        return discountAmount;\n    }\n    \n    private Money calculatePercentDiscountAmount() {\n        return fee.times(discountPercent);\n    }\n    \n    private Money calculateNoneDiscountAmount() {\n        return Money.ZERO;\n    }\n}</code>\n        </deckgo-highlight-code>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">/**\n * 할인 조건 class\n */\npublic class DiscountCondition {\n\n    /**\n     * 할인 조건 타입\n     */\n    private DiscountConditionType type;\n\n    /**\n     * 상영 순번\n     */\n    private int sequence;\n\n    /**\n     * 요일\n     */\n    private DayOfWeek dayOfWeek;\n\n    /**\n     * 시작 시간\n     */\n    private LocalTime startTime;\n\n    /**\n     * 종료 시간\n     */\n    private LocalTime endTime;\n\n    public boolean isSatisfiedBy(Screening screening) {\n        if (type == DiscountConditionType.PERIOD) {\n            return isSatisfiedByPeriod(screening);\n        }\n        \n        return isSatisfiedBySequence(screening);\n    }\n\n    private boolean isSatisfiedByPeriod(Screening screening) {\n        return dayOfWeek.equals(screening.getWhenScreened().getDayOfWeek()) &amp;&amp;\n                !startTime.isAfter(screening.getWhenScreened().toLocalTime()) &amp;&amp;\n                !endTime.isBefore(screening.getWhenScreened().toLocalTime());\n    }\n\n    private boolean isSatisfiedBySequence(Screening screening) {\n        return sequence == screening.getSequence();\n    }\n}</code>\n        </deckgo-highlight-code>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">public enum DiscountConditionType {\n\n    /**\n     * 순번 조건\n     */\n    SEQUENCE,\n\n    /**\n     * 기간 조건\n     */\n    PERIOD\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>위 코드들은 몇 가지 문제점이 숨어있다.</li>\n</ul>\n<h3 id=\"-531-discountcondition-개선하기\" style=\"position:relative;\"><a href=\"#-531-discountcondition-%EA%B0%9C%EC%84%A0%ED%95%98%EA%B8%B0\" aria-label=\" 531 discountcondition 개선하기 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>🔖 5.3.1 DiscountCondition 개선하기</h3>\n<p>가장 큰 문제점은 변경에 취약한 클래스를 포함하고 있다는 것</p>\n<ul>\n<li>변경에 취약한 클래스: 코드를 수정해야 하는 이유를 하나 이상 가지는 클래스</li>\n</ul>\n<p><code>DiscountCondition</code>은 세 가지 이유로 변경될 수 있다.</p>\n<ul>\n<li>새로운 할인 조건 추가</li>\n<li>순번 조건을 판단하는 로직 변경</li>\n<li>기간 조건을 판단하는 로직이 변경되는 경우</li>\n</ul>\n<p>즉, 변경의 이유에 따라 클래스를 분리해야 한다.</p>\n<p>코드를 통해 변경의 이유를 파악할 수 있는 방법</p>\n<ol>\n<li>\n<p><strong>인스턴스 변수가 초기화되는 시점</strong>을 살펴보는 것</p>\n<ul>\n<li>응집도가 높은 클래스는 인스턴스를 생성할 때 모든 속성을 함께 초기화</li>\n<li>함께 초기화되는 속성을 기준으로 코드를 분리해야 한다.</li>\n</ul>\n</li>\n<li>\n<p><strong>메서드들이 인스턴스 변수를 사용하는 방식</strong>을 살펴보는 것</p>\n<ul>\n<li>응집도가 높은 클래스는 모든 메서드가 객체의 모든 속성을 사용</li>\n<li>속성 그룹과 해당 그룹에 접근하는 메서드 그룹을 기준으로 코드를 분리해야 한다.</li>\n</ul>\n</li>\n</ol>\n<h3 id=\"-532-타입-분리하기\" style=\"position:relative;\"><a href=\"#-532-%ED%83%80%EC%9E%85-%EB%B6%84%EB%A6%AC%ED%95%98%EA%B8%B0\" aria-label=\" 532 타입 분리하기 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>🔖 5.3.2 타입 분리하기</h3>\n<p><code>DiscountCondition</code>의 가장 큰 문제는 순번 조건과 기간 조건이라는 두 개의 독립적인 타입이 하나의 클래스 안에 공존하고 있다는 점이다.</p>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">@AllArgsConstructor\npublic class PeriodCondition {\n\n    /**\n     * 요일\n     */\n    private DayOfWeek dayOfWeek;\n\n    /**\n     * 시작 시간\n     */\n    private LocalTime startTime;\n\n    /**\n     * 종료 시간\n     */\n    private LocalTime endTime;\n\n    public boolean isSatisfiedBy(Screening screening) {\n        return dayOfWeek.equals(screening.getWhenScreened().getDayOfWeek()) &amp;&amp;\n                !startTime.isAfter(screening.getWhenScreened().toLocalTime()) &amp;&amp;\n                !endTime.isBefore(screening.getWhenScreened().toLocalTime());\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 SequenceCondition {\n\n    /**\n     * 상영 순번\n     */\n    private int sequence;\n\n    private boolean isSatisfiedBy(Screening screening) {\n        return sequence == screening.getSequence();\n    }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>Movie 또한 수정되어야 하는 문제가 나타났다!</li>\n</ul>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">public class Movie {\n\n    /**\n     * 기간 할인 조건 목록\n     */\n    private List&lt;PeriodCondition&gt; periodConditions;\n\n    /**\n     * 순번 할인 조건 목록\n     */\n    private List&lt;SequenceCondition&gt; sequenceConditions;\n\n    private boolean isDiscountable(Screening screening) {\n        return checkPeriodConditions(screening) || checkSequenceConditions(screening);\n    }\n    \n    private boolean checkPeriodConditions(Screening screening) {\n        return periodConditions.stream().anyMatch(condition -&gt; condition.isSatisfiedBy(screening));\n    }\n\n    private boolean checkSequenceConditions(Screening screening) {\n        return sequenceConditions.stream().anyMatch(condition -&gt; condition.isSatisfiedBy(screening));\n    }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li><code>Movie</code>가 <code>PeriodCondition</code>과 <code>SequenceCondition</code> 양쪽 모두에게 결합된다. 즉, 결합도가 높아졌다.</li>\n<li>새로운 할인 조건을 추가하기가 어려워졌다.</li>\n</ul>\n<h3 id=\"-533-다형성을-통해-분리하기\" style=\"position:relative;\"><a href=\"#-533-%EB%8B%A4%ED%98%95%EC%84%B1%EC%9D%84-%ED%86%B5%ED%95%B4-%EB%B6%84%EB%A6%AC%ED%95%98%EA%B8%B0\" aria-label=\" 533 다형성을 통해 분리하기 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>🔖 5.3.3 다형성을 통해 분리하기</h3>\n<ul>\n<li>역할을 사용하면 객체의 구체적인 타입을 추상화할 수 있다.</li>\n<li>역할을 대체할 클래스들 사이에서 구현을 공유해야 할 필요가 있다면 추상클래스 사용</li>\n<li>구현을 공유할 필요 없이 역할을 대체하는 객체들의 책임만 정의하고 싶다면 인터페이스 사용</li>\n</ul>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">public interface DiscountCondition {\n\n    boolean isSatisfiedBy(Screening screening);\n}</code>\n        </deckgo-highlight-code>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">public class PeriodCondition implements DiscountCondition {...}\npublic class SequenceCondition implements DiscountCondition {...}</code>\n        </deckgo-highlight-code>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">public class Movie {\n\n    /**\n     * 할인 조건 목록\n     */\n    private List&lt;DiscountCondition&gt; discountConditions;\n\n    public Money calculateMovieFee(Screening screening) {\n        if (isDiscountable(screening)) {\n            return fee.minus(calculateDiscountAmount());\n        }\n\n        return fee;\n    }\n\n    private boolean isDiscountable(Screening screening) {\n        return discountConditions.stream().anyMatch(condition -&gt; condition.isSatisfiedBy(screening));\n    }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>객체의 타입에 따라 변하는 행동이 있다면 타입을 분리하고 변화하는 행동을 각 타입의 책임으로 할당한다.\n<ul>\n<li>polymorphism pattern (다형성 패턴)</li>\n</ul>\n</li>\n</ul>\n<h3 id=\"-534-변경으로부터-보호하기\" style=\"position:relative;\"><a href=\"#-534-%EB%B3%80%EA%B2%BD%EC%9C%BC%EB%A1%9C%EB%B6%80%ED%84%B0-%EB%B3%B4%ED%98%B8%ED%95%98%EA%B8%B0\" aria-label=\" 534 변경으로부터 보호하기 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>🔖 5.3.4 변경으로부터 보호하기</h3>\n<p>protected variations pattern (변경 보호 패턴)</p>\n<ul>\n<li>변경을 캡슐화하도록 책임을 할당하는 것</li>\n<li>변하는 것이 무엇인지 고려하고 변하는 개념을 캡슐화하라</li>\n</ul>\n<h3 id=\"-535-movie-클래스-개선하기\" style=\"position:relative;\"><a href=\"#-535-movie-%ED%81%B4%EB%9E%98%EC%8A%A4-%EA%B0%9C%EC%84%A0%ED%95%98%EA%B8%B0\" aria-label=\" 535 movie 클래스 개선하기 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>🔖 5.3.5 Movie 클래스 개선하기</h3>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">public abstract class Movie {\n\n    /**\n     * 제목\n     */\n    private final String title;\n\n    /**\n     * 상영 시간\n     */\n    private final Duration runningTime;\n\n    /**\n     * 기본 요금\n     */\n    private final Money fee;\n\n    /**\n     * 할인 조건 목록\n     */\n    private final List&lt;DiscountCondition&gt; discountConditions;\n\n    protected Movie(String title, Duration runningTime, Money fee, DiscountCondition... discountConditions) {\n        this.title = title;\n        this.runningTime = runningTime;\n        this.fee = fee;\n        this.discountConditions = Arrays.asList(discountConditions);\n    }\n\n    public Money calculateMovieFee(Screening screening) {\n        if (isDiscountable(screening)) {\n            return fee.minus(calculateDiscountAmount());\n        }\n\n        return fee;\n    }\n\n    private boolean isDiscountable(Screening screening) {\n        return discountConditions.stream().anyMatch(condition -&gt; condition.isSatisfiedBy(screening));\n    }\n\n    protected abstract Money calculateDiscountAmount();\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li><code>calculateDiscountAmount</code>를 추상 메서드로 선언</li>\n</ul>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">public class AmountDiscountMovie extends Movie {\n\n    private final Money discountAmount;\n\n    public AmountDiscountMovie(String title, Duration runningTime, Money fee, Money discountAmount, DiscountCondition... discountConditions) {\n        super(title, runningTime, fee, discountConditions);\n        this.discountAmount = discountAmount;\n    }\n\n    @Override\n    protected Money calculateDiscountAmount() {\n        return 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 PercentDiscountMovie extends Movie {\n\n    private final double percent;\n\n    public PercentDiscountMovie(String title, Duration runningTime, Money fee, double percent, DiscountCondition... discountConditions) {\n        super(title, runningTime, fee, discountConditions);\n        this.percent = percent;\n    }\n\n    @Override\n    protected Money calculateDiscountAmount() {\n        return getFee().times(percent);\n    }\n}</code>\n        </deckgo-highlight-code>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">    @Getter(AccessLevel.PROTECTED)\n    private final Money fee;</code>\n        </deckgo-highlight-code>\n<ul>\n<li>이 메서드는 서브클래스에서만 사용해야 하므로 가시성을 protected로 제한해야 한다.</li>\n</ul>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">public class NoneDiscountMovie extends Movie {\n\n    public NoneDiscountMovie(String title, Duration runningTime, Money fee, double percent, DiscountCondition... discountConditions) {\n        super(title, runningTime, fee, discountConditions);\n    }\n\n    @Override\n    protected Money calculateDiscountAmount() {\n        return Money.ZERO;\n    }\n}</code>\n        </deckgo-highlight-code>\n<blockquote>\n<p>도메인의 구조가 코드의 구조를 이끈다</p>\n</blockquote>\n<ul>\n<li>변경 역시 도메인 모델의 일부</li>\n<li>도메인 모델에는 도메인 안에서 변하는 개념과 이들 사이의 관계가 투영돼 있어야 한다.</li>\n<li>구현을 가이드할 수 있는 도메인 모델을 선택하라.</li>\n</ul>\n<h3 id=\"-536-변경과-유연성\" style=\"position:relative;\"><a href=\"#-536-%EB%B3%80%EA%B2%BD%EA%B3%BC-%EC%9C%A0%EC%97%B0%EC%84%B1\" aria-label=\" 536 변경과 유연성 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>🔖 5.3.6 변경과 유연성</h3>\n<p>개발자로서 변경에 대비할 수 있는 두 가지 방법</p>\n<ol>\n<li>\n<p>코드를 이해하고 수정하기 쉽도록 최대한 단순하게 설계</p>\n</li>\n<li>\n<p>코드를 수정하지 않고도 변경을 수용할 수 있도록 코드를 더 유연하게 만드는 것</p>\n<ul>\n<li>유사한 변경이 반복적으로 발생하고 있다면 복잡성이 상승하더라도 유연성을 추가하는 두 번째 방법이 더 좋다.</li>\n</ul>\n</li>\n</ol>\n<ul>\n<li>요소들 사이의 의존성의 정도가 유연성의 정도를 결정</li>\n<li>유연성의 정도에 따라 결합도를 조절할 수 있는 능력은 객체지향 개발자가 갖춰야 하는 중요한 기술 중 하나</li>\n</ul>\n<h2 id=\"-54-책임-주도-설계의-대안\" style=\"position:relative;\"><a href=\"#-54-%EC%B1%85%EC%9E%84-%EC%A3%BC%EB%8F%84-%EC%84%A4%EA%B3%84%EC%9D%98-%EB%8C%80%EC%95%88\" aria-label=\" 54 책임 주도 설계의 대안 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>📖 5.4 책임 주도 설계의 대안</h2>\n<p>이해하기 쉽고 수정하기 쉬운 소프트웨어로 개선하기 위해 겉으로 보이는 동작은 바꾸지 않은 채 내부 구조를 변경하는 것을 리팩터링(Refactoring)이라고 부른다.</p>\n<h3 id=\"-541-메서드-응집도\" style=\"position:relative;\"><a href=\"#-541-%EB%A9%94%EC%84%9C%EB%93%9C-%EC%9D%91%EC%A7%91%EB%8F%84\" aria-label=\" 541 메서드 응집도 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>🔖 5.4.1 메서드 응집도</h3>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">/**\n * 영화 예매 절차 구현 Class\n */\npublic class ReservationAgency {\n\n    public Reservation reserve(Screening screening, Customer customer, int audienceCount) {\n        Movie movie = screening.getMovie();\n\n        boolean discountable = false;\n        for (DiscountCondition condition : movie.getDiscountConditions()) {\n            if (condition.getType() == DiscountConditionType.PERIOD) {\n                discountable = screening.getWhenScreened().getDayOfWeek().equals(condition.getDayOfWeek()) &amp;&amp;\n                        !condition.getStartTime().isAfter(screening.getWhenScreened().toLocalTime()) &amp;&amp;\n                        !condition.getEndTime().isBefore(screening.getWhenScreened().toLocalTime());\n            } else {\n                discountable = condition.getSequence() == screening.getSequence();\n            }\n\n            if (discountable) {\n                break;\n            }\n        }\n\n        Money fee;\n        if (discountable) {\n            Money discountAmount = Money.ZERO;\n            switch (movie.getMovieType()) {\n                case AMOUNT_DISCOUNT -&gt; discountAmount = movie.getDiscountAmount();\n                case PERCENT_DISCOUNT -&gt; discountAmount = movie.getFee().times(movie.getDiscountPercent());\n                case NONE_DISCOUNT -&gt; discountAmount = Money.ZERO;\n            }\n\n            fee = movie.getFee().minus(discountAmount);\n        } else {\n            fee = movie.getFee();\n        }\n\n        return new Reservation(customer, screening, fee, audienceCount);\n    }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>reserve 메서드는 길이가 너무 길고 이해하기도 어렵다.</li>\n<li>긴 메서드는 다양한 측면에서 코드의 유지보수에 부정적인 영향을 미친다.\n<ul>\n<li>어떤 일을 수행하는지 한눈에 파악하기 어렵기 때문에 코드를 전체적으로 이해하는 데 너무 많은 시간이 걸린다.</li>\n<li>하나의 메서드 안에서 너무 많은 작업을 처리하기 때문에 변경이 필요할 때 수정해야 할 부분을 찾기 어렵다.</li>\n<li>메서드 내부의 일부 로직만 수정하더라도 메서드의 나머지 부분에서 버그가 발생할 확률이 높다.</li>\n<li>로직의 일부만 재사용하는 것이 불가능하다.</li>\n<li>코드를 재사용하는 유일한 방법은 원하는 코드를 복사해서 분여넣는 것뿐이므로 코드 중복을 초래하기 쉽다.</li>\n</ul>\n</li>\n<li>이런 메서드를 <strong>몬스터 메서드</strong> 라고 부르기도 한다.</li>\n<li>작은 메서드들로 조합된 메서드는 마치 주석들을 나열한 것처럼 보이기 때문에 코드를 이해하기도 쉽다.\n<ul>\n<li>이름을 잘 지어야 한다!</li>\n<li>이름의 길이보다는 의미가 중요하다!</li>\n</ul>\n</li>\n</ul>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">public class ReservationAgency {\n\n    public Reservation reserve(Screening screening, Customer customer, int audienceCount) {\n        boolean discountable = checkDiscountable(screening);\n        Money fee = calculateFee(screening, discountable, audienceCount);\n        return createReservation(screening, customer, audienceCount, fee);\n    }\n\n    private boolean checkDiscountable(Screening screening) {\n        return screening.getMovie().getDiscountConditions().stream().anyMatch(condition -&gt; isDiscountable(condition, screening));\n    }\n\n    private boolean isDiscountable(DiscountCondition condition, Screening screening) {\n        if (condition.getType() == DiscountConditionType.PERIOD) {\n            return isSatisfiedByPeriod(condition, screening);\n        }\n        return isSatisfiedBySequence(condition, screening);\n    }\n\n    private boolean isSatisfiedByPeriod(DiscountCondition condition, Screening screening) {\n        return screening.getWhenScreened().getDayOfWeek().equals(condition.getDayOfWeek()) &amp;&amp;\n                !condition.getStartTime().isAfter(screening.getWhenScreened().toLocalTime()) &amp;&amp;\n                !condition.getEndTime().isBefore(screening.getWhenScreened().toLocalTime());\n    }\n\n    private boolean isSatisfiedBySequence(DiscountCondition condition, Screening screening) {\n        return condition.getSequence() == screening.getSequence();\n    }\n\n    private Money calculateFee(Screening screening, boolean discountable, int audienceCount) {\n        if (discountable) {\n            return screening.getMovie().getFee()\n                    .minus(calculateDiscountedFee(screening.getMovie()))\n                    .times(audienceCount);\n        }\n        \n        return screening.getMovie().getFee().times(audienceCount);\n    }\n\n    private Money calculateDiscountedFee(Movie movie) {\n        switch (movie.getMovieType()) {\n            case AMOUNT_DISCOUNT -&gt; {\n                return calculateAmountDiscountedFee(movie);\n            }\n            case PERCENT_DISCOUNT -&gt; {\n                return calculatePercentDiscountedFee(movie);\n            }\n            case NONE_DISCOUNT -&gt; {\n                return calculateNoneDiscountedFee();\n            }\n        }\n\n        throw new IllegalArgumentException();\n    }\n\n    private Money calculateAmountDiscountedFee(Movie movie) {\n        return movie.getDiscountAmount();\n    }\n\n    private Money calculatePercentDiscountedFee(Movie movie) {\n        return movie.getFee().times(movie.getDiscountPercent());\n    }\n\n    private Money calculateNoneDiscountedFee() {\n        return Money.ZERO;\n    }\n    \n    private Reservation createReservation(Screening screening, Customer customer, int audienceCount, Money fee) {\n        return new Reservation(customer, screening, fee, audienceCount);\n    }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>수정 후 에는 메서드가 어떤 일을 하는지를 한눈에 알아볼 수 있다.</li>\n<li>하지만 여전히 응집도는 낮다.</li>\n</ul>\n<h3 id=\"-542-객체를-자율적으로-만들자\" style=\"position:relative;\"><a href=\"#-542-%EA%B0%9D%EC%B2%B4%EB%A5%BC-%EC%9E%90%EC%9C%A8%EC%A0%81%EC%9C%BC%EB%A1%9C-%EB%A7%8C%EB%93%A4%EC%9E%90\" aria-label=\" 542 객체를 자율적으로 만들자 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>🔖 5.4.2 객체를 자율적으로 만들자</h3>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">public class DiscountCondition {\n\n    /**\n     * 할인 조건 타입\n     */\n    private DiscountConditionType type;\n\n    /**\n     * 상영 순번\n     */\n    private int sequence;\n\n    /**\n     * 요일\n     */\n    private DayOfWeek dayOfWeek;\n\n    /**\n     * 시작 시간\n     */\n    private LocalTime startTime;\n\n    /**\n     * 종료 시간\n     */\n    private LocalTime endTime;\n\n    public boolean isDiscountable(Screening screening) {\n        if (type == DiscountConditionType.PERIOD) {\n            return isSatisfiedByPeriod(screening);\n        }\n        return isSatisfiedBySequence(screening);\n    }\n\n    private boolean isSatisfiedByPeriod(Screening screening) {\n        return screening.getWhenScreened().getDayOfWeek().equals(dayOfWeek) &amp;&amp;\n                !startTime.isAfter(screening.getWhenScreened().toLocalTime()) &amp;&amp;\n                !endTime.isBefore(screening.getWhenScreened().toLocalTime());\n    }\n\n    private boolean isSatisfiedBySequence(Screening screening) {\n        return sequence == screening.getSequence();\n    }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li><code>DiscountCondition</code>으로 이동하고, 접근제어자를 변경했다.</li>\n</ul>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">    private boolean checkDiscountable(Screening screening) {\n        return screening.getMovie().getDiscountConditions().stream().anyMatch(condition -&gt; condition.isDiscountable(screening));\n    }</code>\n        </deckgo-highlight-code>\n<ul>\n<li>결국 데이터 중심으로 구현한 후 이를 리팩터링하더라도 유사한 결과를 얻을 수 있다.</li>\n<li>처음부터 책임 주도 설계 방법을 따르는 것보다 동작하는 코드를 작성한 후에 리팩터링하는 것이 더 훌륭한 결과물을 낳을 수도 잇다.</li>\n</ul>","excerpt":"책임 할당 과정은 일종의 trade-off 활동이다. 📖 5.1 책임 주도 설계를 향해 데이터보다 행동을 먼저 결정하라 협력이라는 문맥 안에서 책임을 결정하라 🔖 5.1.…","fields":{"slug":"/backend/object/chapter5/책임_할당하기/"},"frontmatter":{"title":"Object - 5장 책임 할당하기","thumbnail":{"childImageSharp":{"fluid":{"src":"/static/dd0e1990925e942697544e2bdcd9332e/9b73b/object.png"}}},"draft":false,"category":"Back-End","tags":["java"],"date":"September 24, 2023"}},"previous":{"id":"5c30d890-7e1f-5638-9630-e5d1dea7f6c9","html":"<p>사람의 기억은 단기 기억과 장기 기억으로 분류할 수 있다. 실제로 문제를 해결하기 위해 사용하는 저장소는 장기 기억이 아니라 단기 기억이다.</p>\n<ul>\n<li>인지 과부하(cognitive overload): 문제 해결에 필요한 요소의 수가 단기 기억의 용량을 초과하는 순간 문제 해결 능력이 급격하게 떨어지는 것</li>\n<li>인지 과부하를 줄이는 가장 좋은 방법은 단기 기억 안에 보관할 정보의 양을 조절하는 것\n<ul>\n<li>추상화: 불필요한 정보를 제거하고 현재의 문제 해결에 필요한 핵심만 남기는 작업</li>\n<li>분해(decomposition): 큰 문제를 해결 가능한 작은 문제로 나누는 작업</li>\n</ul>\n</li>\n</ul>\n<blockquote>\n<p>한 번에 단기 기억에 담을 수 있는 추상화의 수에는 한계가 있지만 추상화를 더 큰 규모의 추상화로 압축시킴으로써 단기 기억의 한계를 초월할 수 있다.</p>\n</blockquote>\n<h2 id=\"-71-프로시저-추상화와-데이터-추상화\" style=\"position:relative;\"><a href=\"#-71-%ED%94%84%EB%A1%9C%EC%8B%9C%EC%A0%80-%EC%B6%94%EC%83%81%ED%99%94%EC%99%80-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%B6%94%EC%83%81%ED%99%94\" aria-label=\" 71 프로시저 추상화와 데이터 추상화 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>📖 7.1 프로시저 추상화와 데이터 추상화</h2>\n<p>모든 프로그래밍 패러다임은 추상화와 분해의 관점에서 설명할 수 있다.</p>\n<ol>\n<li>프로시저 추상화\n<ul>\n<li>소프트웨어가 무엇을 해야하는지를 추상화</li>\n<li>프로시저 추상화를 중심으로 시스템을 분해한다면 기능 분해(알고리즘 분해)</li>\n</ul>\n</li>\n<li>데이터 추상화\n<ul>\n<li>소프트웨어가 무엇을 알아야 하는지를 추상화</li>\n<li>데이터 추상화를 중심으로 시스템을 분해한다면 <strong>타입을 추상화(추상 데이터 타입)</strong> 하거나 <strong>프로시저를 추상화(객체지향)</strong> 해야한다.</li>\n</ul>\n</li>\n</ol>\n<p><code>협력하는 공동체</code>를 구성하도록 객체들로 나누는 과정이 바로 객체지향 패러다임에서의 분해를 의미</p>\n<h2 id=\"-72-프로시저-추상화와-기능-분해\" style=\"position:relative;\"><a href=\"#-72-%ED%94%84%EB%A1%9C%EC%8B%9C%EC%A0%80-%EC%B6%94%EC%83%81%ED%99%94%EC%99%80-%EA%B8%B0%EB%8A%A5-%EB%B6%84%ED%95%B4\" aria-label=\" 72 프로시저 추상화와 기능 분해 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>📖 7.2 프로시저 추상화와 기능 분해</h2>\n<h3 id=\"-721-메인-함수로의-시스템\" style=\"position:relative;\"><a href=\"#-721-%EB%A9%94%EC%9D%B8-%ED%95%A8%EC%88%98%EB%A1%9C%EC%9D%98-%EC%8B%9C%EC%8A%A4%ED%85%9C\" aria-label=\" 721 메인 함수로의 시스템 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>🔖 7.2.1 메인 함수로의 시스템</h3>\n<p>기능 분해의 관점에서 추상화의 단위는 프로시저이며 시스템은 프로시저를 단위로 분해</p>\n<ul>\n<li>프로시저는 반복적으로 실행되거나 거의 유사하게 실행되는 작업들을 하나의 장소에 모아놓음으로써 로직을 재사용하고 중복을 방지할 수 있는 추상화 방법</li>\n<li>프로시저는 잠재적으로 정보은닉의 가능성을 제시</li>\n</ul>\n<blockquote>\n<p>시스템은 필요한 더 작은 작업으로 분해될 수 있는 하나의 커다란 메인 함수다.</p>\n</blockquote>\n<p>전통적인 기능 분해 방법은 **하향식 접근법(Top-Down Approach)**을 따른다.</p>\n<ul>\n<li>가장 최상위 기능을 정의하고, 이 기능을 좀 더 작은 단계의 하위 기능으로 분해해 나가는 방법</li>\n<li>분해는 세분화된 마지막 하위 기능이 프로그래밍 언어로 구현 가능한 수준이 될 때까지 계속된다.</li>\n</ul>\n<h3 id=\"-722-급여-관리-시스템\" style=\"position:relative;\"><a href=\"#-722-%EA%B8%89%EC%97%AC-%EA%B4%80%EB%A6%AC-%EC%8B%9C%EC%8A%A4%ED%85%9C\" aria-label=\" 722 급여 관리 시스템 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>🔖 7.2.2 급여 관리 시스템</h3>\n<p><code>급여 = 기본급 - (기본급 * 소득세율)</code></p>\n<p><code>직원의 급여를 계산한다</code></p>\n<ul>\n<li>급여 관리 시스템에 대한 추상적인 최상위 문장</li>\n</ul>\n<deckgo-highlight-code language=\"text\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">직원의 급여를 계산한다\n  사용자로부터 소득세율을 입력받는다.\n  직원의 급여를 계산한다.\n  양식에 맞게 결과를 출력한다.</code>\n        </deckgo-highlight-code>\n<ul>\n<li>세부적인 절차로 구체화</li>\n</ul>\n<deckgo-highlight-code language=\"text\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">직원의 급여를 계산한다\n  사용자로부터 소득세율을 입력받는다.\n    &quot;세율을 입력하세요: &quot;라는 문장을 화면에 출력한다.\n    키보드를 통해 세율을 입력받는다.\n  직원의 급여를 계산한다.\n    전역 변수에 저장된 직원의 기본급 정보를 얻는다.\n    급여를 계산한다.\n  양식에 맞게 결과를 출력한다.\n    &quot;이름: {직원명}, 급여: {계산된 금액}&quot; 형식에 다라 출력 문자열을 생성한다.</code>\n        </deckgo-highlight-code>\n<ul>\n<li>기능 분해의 결과</li>\n<li>입력 정보는 직원정보와 소득세율이고 출력은 계산된 급여 정보</li>\n</ul>\n<h3 id=\"-723-급여-관리-시스템-구현\" style=\"position:relative;\"><a href=\"#-723-%EA%B8%89%EC%97%AC-%EA%B4%80%EB%A6%AC-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EA%B5%AC%ED%98%84\" aria-label=\" 723 급여 관리 시스템 구현 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>🔖 7.2.3 급여 관리 시스템 구현</h3>\n<p>루비 언어를 기반으로 예시를 든다.</p>\n<p><code>직원의 급여를 계산한다</code></p>\n<deckgo-highlight-code language=\"ruby\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">def main(name)\nend</code>\n        </deckgo-highlight-code>\n<ul>\n<li>최상위 문장은 하나의 메인 함수로 매핑된다.</li>\n</ul>\n<deckgo-highlight-code language=\"text\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">  사용자로부터 소득세율을 입력받는다.\n  직원의 급여를 계산한다.\n  양식에 맞게 결과를 출력한다.</code>\n        </deckgo-highlight-code>\n<deckgo-highlight-code language=\"ruby\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">def main(name)\n  taxRate = getTaxRate()\n  pay = calculatePayFor(name, taxRate)\n  puts(describeResult(name, pay))\nend</code>\n        </deckgo-highlight-code>\n<ul>\n<li>각 문장에 대응된다.</li>\n</ul>\n<deckgo-highlight-code language=\"text\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">  사용자로부터 소득세율을 입력받는다.\n    &quot;세율을 입력하세요: &quot;라는 문장을 화면에 출력한다.\n    키보드를 통해 세율을 입력받는다.</code>\n        </deckgo-highlight-code>\n<deckgo-highlight-code language=\"ruby\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">def getTaxRate()\n  print(&quot;세율을 입력하세요: &quot;)\n  return gets().chomp().to_f()\nend</code>\n        </deckgo-highlight-code>\n<deckgo-highlight-code language=\"text\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">  직원의 급여를 계산한다.\n    전역 변수에 저장된 직원의 기본급 정보를 얻는다.\n    급여를 계산한다.</code>\n        </deckgo-highlight-code>\n<deckgo-highlight-code language=\"ruby\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">$employees = [&quot;직원A&quot;, &quot;직원B&quot;, &quot;직원C&quot;]\n$basePays = [400, 300, 250]</code>\n        </deckgo-highlight-code>\n<ul>\n<li>동일한 직원에 대한 이름과 기본급 정보는 두 배열 내의 동일한 인덱스에 저장</li>\n</ul>\n<deckgo-highlight-code language=\"ruby\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">def calculatePayFor(name, taxRate)\n  index = $imployees.index(name)\n  basePay = $basePays[index]\n  return basePay - (basePay * taxRate)\nend</code>\n        </deckgo-highlight-code>\n<deckgo-highlight-code language=\"text\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">  양식에 맞게 결과를 출력한다.\n    &quot;이름: {직원명}, 급여: {계산된 금액}&quot; 형식에 다라 출력 문자열을 생성한다.</code>\n        </deckgo-highlight-code>\n<deckgo-highlight-code language=\"ruby\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">def describeResult(name, pay)\n  return &quot;이름: #{name}, 급여: #{pay}&quot;\nend</code>\n        </deckgo-highlight-code>\n<p>하향식 기능 분해 방식으로 설계한 시스템은 메인 함수를 루트로 하는 '트리(tree)'로 표현할 수 있다. 체계적이고 이상적으로 보이지만 우리가 사는 세계는 그렇게 체계적이지도, 이상적이지도 않다.</p>\n<h3 id=\"-724-하향식-기능-분해의-문제점\" style=\"position:relative;\"><a href=\"#-724-%ED%95%98%ED%96%A5%EC%8B%9D-%EA%B8%B0%EB%8A%A5-%EB%B6%84%ED%95%B4%EC%9D%98-%EB%AC%B8%EC%A0%9C%EC%A0%90\" aria-label=\" 724 하향식 기능 분해의 문제점 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>🔖 7.2.4 하향식 기능 분해의 문제점</h3>\n<ul>\n<li>시스템은 하나의 메인 함수로 구성돼 있지 않다.</li>\n<li>기능 추가나 요구사항 변경으로 인해 메인 함수를 빈번하게 수정해야 한다.</li>\n<li>비즈니스 로직이 사용자 인터페이스와 강하게 결합된다.</li>\n<li>하향식 분해는 너무 이른 시기에 함수들의 실행 순서를 고정시키기 때문에 유연성과 재사용성이 저하된다.</li>\n<li>데이터 형식이 변경될 경우 파급효과를 예측할 수 없다.</li>\n</ul>\n<h4 id=\"-하나의-메인-함수라는-비현실적인-아이디어\" style=\"position:relative;\"><a href=\"#-%ED%95%98%EB%82%98%EC%9D%98-%EB%A9%94%EC%9D%B8-%ED%95%A8%EC%88%98%EB%9D%BC%EB%8A%94-%EB%B9%84%ED%98%84%EC%8B%A4%EC%A0%81%EC%9D%B8-%EC%95%84%EC%9D%B4%EB%94%94%EC%96%B4\" 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<ul>\n<li>어떤 시스템도 최초에 release됐던 당시의 모습을 그대로 유지하지는 않는다. 즉, 지속적으로 새로운 기능을 추가하게 된다.</li>\n<li>대부분의 추가되는 기능은 메인 함수의 일부가 아닐 것이다. 결국 어느 시점에 이르면 유일한 메인 함수라는 개념은 의미가 없어지고 시스템은 여러 개의 동등한 수준의 함수 집합으로 성장하게 될 것이다.</li>\n<li>하향식 접근법은 하나의 알고리즘을 구현하거나 배치 처리를 구현하기에는 적합하지만 현대적인 상호작용 시스템을 개발하는 데는 적합하지 않다.</li>\n</ul>\n<blockquote>\n<p>실제 시스템에 정상(top)이란 존재하지 않는다.      - 버트란드 마이어</p>\n</blockquote>\n<h4 id=\"-메인-함수의-빈번한-재설계\" style=\"position:relative;\"><a href=\"#-%EB%A9%94%EC%9D%B8-%ED%95%A8%EC%88%98%EC%9D%98-%EB%B9%88%EB%B2%88%ED%95%9C-%EC%9E%AC%EC%84%A4%EA%B3%84\" 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<ul>\n<li>기존 로직과는 아무런 상관이 없는 새로운 함수의 적절한 위치를 확보해야 하기 때문에 메인 함수의 구조를 급격하게 변경할 수밖에 없다.</li>\n</ul>\n<p>급여 관리 시스템에 모든 직원들의 기본급의 총합을 구하는 기능을 추가해 달라는 새로운 요구사항이 접수됐다고 가정</p>\n<deckgo-highlight-code language=\"ruby\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">def sumOfBasePays()\n  result = 0\n  for basePay in $basePays\n    result += basePay\n  end\n  puts(result)\nend</code>\n        </deckgo-highlight-code>\n<deckgo-highlight-code language=\"ruby\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">def main(name)\n  taxRate = getTaxRate()\n  pay = calculatePayFor(name, taxRate)\n  puts(describeResult(name, pay))\nend</code>\n        </deckgo-highlight-code>\n<ul>\n<li>메인함수 안에서 호출할 자리가 마땅치 않다.</li>\n</ul>\n<deckgo-highlight-code language=\"ruby\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">def calculatePay(name)\n  taxRate = getTaxRate()\n  pay = calculatePayFor(name, taxRate)\n  puts(describeResult(name, pay))\nend</code>\n        </deckgo-highlight-code>\n<ul>\n<li>메인함수를 <code>calcuatePay</code> 함수로 옮기자</li>\n</ul>\n<deckgo-highlight-code language=\"ruby\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">def main(operation, args={})\n  case(operation)\n  when :pay then calculatePay(args[:name])\n  when :basePays then sumOfBasePays()\n  end\nend</code>\n        </deckgo-highlight-code>\n<ul>\n<li>인자에 따라 다른 함수를 호출</li>\n<li>결과적으로 기존 코드의 빈번한 수정으로 인한 버그 발생 확률이 높아지기 때문에 시스템 변경에 취약해질 수 밖에 없다.</li>\n</ul>\n<h4 id=\"-비즈니스-로직과-사용자-인터페이스의-결합\" style=\"position:relative;\"><a href=\"#-%EB%B9%84%EC%A6%88%EB%8B%88%EC%8A%A4-%EB%A1%9C%EC%A7%81%EA%B3%BC-%EC%82%AC%EC%9A%A9%EC%9E%90-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4%EC%9D%98-%EA%B2%B0%ED%95%A9\" 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<ul>\n<li>비즈니스 로직을 설계하는 초기 단계부터 입력 방법과 출력 양식을 함께 고민하도록 강요</li>\n<li>코드 안에서 비즈니스 로직과 사용자 인터페이스 로직이 밀접하게 결합\n<ul>\n<li>사용자 인터페이스가 변경되는 빈번도 ⏫</li>\n<li>비즈니스 로직이 변경되는 빈번도 ⏬</li>\n<li>사용자 인터페이스가 변경되는 경우 비즈니스 로직까지 변경에 영향을 받음</li>\n<li>근본적으로 변경에 불안정한 아키텍처를 낳는다.</li>\n</ul>\n</li>\n<li>하향식 접근법은 기능을 분해하는 과정에서 사용자 인터페이스의 관심사와 비즈니스 로직의 관심사를 동시에 고려하도록 강요하기 때문에 \"관심사의 분리\"라는 아키텍처 설계의 목적을 달성하기 어렵다.</li>\n</ul>\n<h4 id=\"-성급하게-결정된-실행-순서\" style=\"position:relative;\"><a href=\"#-%EC%84%B1%EA%B8%89%ED%95%98%EA%B2%8C-%EA%B2%B0%EC%A0%95%EB%90%9C-%EC%8B%A4%ED%96%89-%EC%88%9C%EC%84%9C\" 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<ul>\n<li>하향식 기능 분해는 설계를 시작하는 시점에서 시스템이 무엇(what)을 해야 하는지가 아니라 어떻게(how) 동작해야 하는지에 집중하도록 만든다.</li>\n<li>처음부터 구현을 염두에 두기 때문에 함수들의 실행 순서를 정의하는 시간 제약을 강조</li>\n<li>중앙집중 제어 스타일의 형태를 띈다.</li>\n<li>하향식 접근법을 통해 분해한 함수들은 재사용이 어렵다.</li>\n</ul>\n<p>하향식 설계와 관련된 모든 문제의 원인은 <strong>결합도</strong> ❗️</p>\n<ul>\n<li>전체 시스템의 핵심적인 구조를 결정하는 함수들이 데이터와 강하게 결합된다는 것</li>\n</ul>\n<h4 id=\"-데이터-변경으로-인한-파급효과\" style=\"position:relative;\"><a href=\"#-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%B3%80%EA%B2%BD%EC%9C%BC%EB%A1%9C-%EC%9D%B8%ED%95%9C-%ED%8C%8C%EA%B8%89%ED%9A%A8%EA%B3%BC\" 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<ul>\n<li>하향식 기능 분해의 가장 큰 문제점은 어떤 데이터를 어떤 함수가 사용하고 있는지를 추적하기 어렵다는 것이다.\n<ul>\n<li>데이터 변경으로 인해 어떤 함수가 영향을 받을지 예상하기 어렵다.</li>\n</ul>\n</li>\n</ul>\n<p>정규 직원의 급여뿐만 아니라 아르바이트 직원에 대한 급여도 관리를 할 수 있도록 해달라는 변경 요청이 왔다고 가정</p>\n<ul>\n<li>아르바이트 직원은 시간에 시급을 곱한 금액만큼을 지급</li>\n</ul>\n<deckgo-highlight-code language=\"ruby\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">$employees = [&quot;직원A&quot;, &quot;직원B&quot;, &quot;직원C&quot;, &quot;아르바이트D&quot;, &quot;아르바이트E&quot;, &quot;아르바이트F&quot;]\n$basePays = [400, 300, 250, 1, 1, 1.5]\n$hourlys = [false, false, false, true, true, true]\n$timeCards = [0, 0, 0, 120, 120, 120]</code>\n        </deckgo-highlight-code>\n<ul>\n<li>hourlys: true: 아르바이트, false: 정규직원</li>\n<li>timeCards: 한달 간의 업무 누적 시간</li>\n</ul>\n<deckgo-highlight-code language=\"ruby\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">def calculateHourlyPayFor(name, taxRate)\n  index = $imployees.index(name)\n  basePay = $basePays[index] * $timeCards[index]\n  return basePay - (basePay * taxRate)\nend</code>\n        </deckgo-highlight-code>\n<ul>\n<li>아르바이트 직원의 급여를 계산하는 함수</li>\n</ul>\n<deckgo-highlight-code language=\"ruby\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">def hourly?(name)\n  return $hourlys[$employees.index(name)]\nend</code>\n        </deckgo-highlight-code>\n<ul>\n<li>정규직원과 아르바이트 직원을 판단하는 함수</li>\n</ul>\n<deckgo-highlight-code language=\"ruby\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">def calculatePay(name)\n  taxRate = getTaxRate()\n  if (hourly?(name)) then\n    pay = calculateHourlyPayFor(name, taxRate)\n  else\n    pay = calculatePayFor(name, taxRate)\n  end\n  puts(describeResult(name, pay))\nend</code>\n        </deckgo-highlight-code>\n<deckgo-highlight-code language=\"ruby\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">def sumOfBasePays()\n  result = 0\n  for name in $employees\n    if (not hourly?(name)) then\n      result += $basePays[$employees.index(name)]\n    end\n  end\n  puts(result)\nend</code>\n        </deckgo-highlight-code>\n<p>이처럼 데이터 변경으로 인해 발생하는 함수에 대한 영향도를 파악하는 것이 쉽지 않다.</p>\n<ul>\n<li>변경에 대한 영향을 최소화하기 위해 영향을 받는 부분과 받지 않는 부분을 명확하게 분리하고 잘 정의된 퍼블릭 인터페이스를 통해 변경되는 부분에 대한 접근을 통제하는 것이 의존성 관리의 핵심이다.</li>\n</ul>\n<h3 id=\"-725-언제-하향식-분해가-유용한가\" style=\"position:relative;\"><a href=\"#-725-%EC%96%B8%EC%A0%9C-%ED%95%98%ED%96%A5%EC%8B%9D-%EB%B6%84%ED%95%B4%EA%B0%80-%EC%9C%A0%EC%9A%A9%ED%95%9C%EA%B0%80\" aria-label=\" 725 언제 하향식 분해가 유용한가 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>🔖 7.2.5 언제 하향식 분해가 유용한가?</h3>\n<p>하향식 설계는 설계가 어느 정도 안정화 된 후에는 설계의 다양한 측면을 논리적으로 설명하고 문서화하기에 용이하다.</p>\n<blockquote>\n<p>하향식은 이미 완전히 이해된 사실을 서술하기에 적합한 방법이다.</p>\n</blockquote>\n<ul>\n<li>하향식 분해는 작은 프로그램과 개별 알고리즘을 위해서는 유용하다.</li>\n<li>이미 해결된 알고리즘을 문서화하고 서술하는 데 유용하다.</li>\n</ul>\n<h2 id=\"-73-모듈\" style=\"position:relative;\"><a href=\"#-73-%EB%AA%A8%EB%93%88\" aria-label=\" 73 모듈 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>📖 7.3 모듈</h2>\n<h3 id=\"-731-정보-은닉과-모듈\" style=\"position:relative;\"><a href=\"#-731-%EC%A0%95%EB%B3%B4-%EC%9D%80%EB%8B%89%EA%B3%BC-%EB%AA%A8%EB%93%88\" aria-label=\" 731 정보 은닉과 모듈 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>🔖 7.3.1 정보 은닉과 모듈</h3>\n<p><strong>정보 은닉</strong>은 시스템을 모듈 단위로 분해하기 위한 기본 원리로 시스템에서 자주 변경되는 부분을 상대적으로 덜 변경되는 안정적인 인터페이스 뒤로 감춰야 한다는 것이 핵심이다.</p>\n<blockquote>\n<p>모듈은 서브 프로그램이라기보다는 책임의 할당이다.</p>\n</blockquote>\n<ul>\n<li>기능 분해는 하나의 기능을 구현하기 위해 필요한 기능들을 순차적으로 찾아가는 탐색의 과정</li>\n<li>모듈 분해는 감춰야 하는 비밀을 선택하고 비밀 주변에 안정적인 보호막을 설치하는 보존의 과정</li>\n</ul>\n<p>모듈이 감춰야 할 비밀</p>\n<ol>\n<li>복잡성</li>\n<li>변경 가능성</li>\n</ol>\n<deckgo-highlight-code language=\"ruby\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">module Employees\n  $employees = [&quot;직원A&quot;, &quot;직원B&quot;, &quot;직원C&quot;, &quot;아르바이트D&quot;, &quot;아르바이트E&quot;, &quot;아르바이트F&quot;]\n  $basePays = [400, 300, 250, 1, 1, 1.5]\n  $hourlys = [false, false, false, true, true, true]\n  $timeCards = [0, 0, 0, 120, 120, 120]\n\n  def Employees.calculatePay(name, taxRate)\n    if (Employees.hourly?(name)) then\n      pay = calculateHourlyPayFor(name, taxRate)\n    else\n      pay = calculatePayFor(name, taxRate)\n    end\n  end\n\n  def Employees.hourly?(name)\n    return $hourlys[$employees.index(name)]\n  end\n\n  def Employees.calculateHourlyPayFor(name, taxRate)\n    index = $imployees.index(name)\n    basePay = $basePays[index] * $timeCards[index]\n    return basePay - (basePay * taxRate)\n  end\n\n  def Employees.calculatePayFor(name, taxRate)\n    return basePay - (basePay * taxRate)\n  end\n\n  def Employees.sumOfBasePays()\n    result = 0\n    for name in $employees\n      if (not Employees.hourly?(name)) then\n        result += $basePays[$employees.index(name)]\n      end\n    end\n    return result\n  end</code>\n        </deckgo-highlight-code>\n<ul>\n<li>ruby 언어는 <code>module</code>이라는 키워드를 제공하지만 모듈은 키워드의 지원 여부와 상관없이 적용할 수 있는 논리적인 개념이다.</li>\n<li>모듈 외부에서는 모듈 내부에 어떤 데이터가 존재하는지 알 수 없다.</li>\n</ul>\n<deckgo-highlight-code language=\"ruby\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">def main(operation, args={})\n  case(operation)\n  when :pay then calculatePay(args[:name])\n  when :basePays then sumOfBasePays()\n  end\nend\n\ndef calculatePay(name)\n  taxRate = getTaxRate()\n  pay = Employees.calculatePayFor(name, taxRate)\n  puts(describeResult(name, pay))\nend\n\ndef getTaxRate()\n  print(&quot;세율을 입력하세요: &quot;)\n  return gets().chomp().to_f()\nend\n\ndef describeResult(name, pay)\n  return &quot;이름: #{name}, 급여: #{pay}&quot;\nend\n\ndef sumOfBasePays()\n  puts(Employees.sumOfBasePays())\nend</code>\n        </deckgo-highlight-code>\n<h3 id=\"-732-모듈의-장점과-한계\" style=\"position:relative;\"><a href=\"#-732-%EB%AA%A8%EB%93%88%EC%9D%98-%EC%9E%A5%EC%A0%90%EA%B3%BC-%ED%95%9C%EA%B3%84\" aria-label=\" 732 모듈의 장점과 한계 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>🔖 7.3.2 모듈의 장점과 한계</h3>\n<p>모듈의 장점</p>\n<ul>\n<li>모듈 내부의 변수가 변경되더라도 모듈 내부에만 영향을 미친다.\n<ul>\n<li>데이터 변경으로 인한 파급효과를 제어할 수 있으므로 코드를 수정하고 디버깅이 용이</li>\n</ul>\n</li>\n<li>비즈니스 로직과 사용자 인터페이스에 대한 관심사를 분리한다.\n<ul>\n<li>사용자 인터페이스가 변경되어도 비즈니스 로직은 변경되지 않는다.</li>\n</ul>\n</li>\n<li>전역 변수와 전역 함수를 제거함으로써 네임스페이스 오염을 방지한다.\n<ul>\n<li>모듈은 네임스페이스를 제공</li>\n<li>이름 충돌의 위험 방지</li>\n</ul>\n</li>\n</ul>\n<p>모듈은 기능이 아니라 변경의 정도에 따라 시스템을 분해하게 한다.</p>\n<ul>\n<li>높은 응집도\n<ul>\n<li>비밀과 관련성 높은 데이터의 집합</li>\n</ul>\n</li>\n<li>낮은 결합도\n<ul>\n<li>모듈과 모듈은 퍼블릭 인터페이스를 통해서만 통신</li>\n</ul>\n</li>\n</ul>\n<p>모듈은 데이터와 함수가 통합된 한 차원 높은 추상화를 제공하는 설계 단위이다 ❗️</p>\n<p>모듈의 가장 큰 단점은 인스턴스의 개념을 제공하지 않는다는 점이다. 다수의 직원 인스턴스가 존재하는 추상화 메커니즘이 추상 데이터 타입이다.</p>\n<h2 id=\"-74-데이터-추상화와-추상-데이터-타입\" style=\"position:relative;\"><a href=\"#-74-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%B6%94%EC%83%81%ED%99%94%EC%99%80-%EC%B6%94%EC%83%81-%EB%8D%B0%EC%9D%B4%ED%84%B0-%ED%83%80%EC%9E%85\" aria-label=\" 74 데이터 추상화와 추상 데이터 타입 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>📖 7.4 데이터 추상화와 추상 데이터 타입</h2>\n<h3 id=\"-741-추상-데이터-타입\" style=\"position:relative;\"><a href=\"#-741-%EC%B6%94%EC%83%81-%EB%8D%B0%EC%9D%B4%ED%84%B0-%ED%83%80%EC%9E%85\" aria-label=\" 741 추상 데이터 타입 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>🔖 7.4.1 추상 데이터 타입</h3>\n<p>프로그래밍 언어에서 <strong>타입(type)</strong> 이란 변수에 저장할 수 있는 내용물의 종류와 변수에 적용될 수 있는 연산의 가짓수를 의미한다.</p>\n<p>추상 데이터 타입을 구현하려면 다음과 같은 특성을 위한 프로그래밍 언어의 지원이 필요하다.</p>\n<ul>\n<li>타입 정의를 선언할 수 있어야 한다.</li>\n<li>타입의 인스턴스를 다루기 위해 사용할 수 있는 오퍼레이션의 집합을 정의할 수 있어야 한다.</li>\n<li>제공된 오퍼레이션을 통해서만 조작할 수 있도록 데이터를 외부로부터 보호할 수 있어야 한다.</li>\n<li>타입에 대해 여러 개의 인스턴스를 생성할 수 있어야 한다.</li>\n</ul>\n<p>리스코프는 추상 데이터 타입을 정의하기 위해 제시한 언어적인 메커니즘을 오퍼레이션 클러스터라고 불렀다.</p>\n<deckgo-highlight-code language=\"ruby\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">Employee = Struct.new(:name, :basePay, :hourly, :timeCard) do\nEnd</code>\n        </deckgo-highlight-code>\n<ul>\n<li>개별 직원을 위한 추상 데이터 타입</li>\n</ul>\n<deckgo-highlight-code language=\"ruby\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">Employee = Struct.new(:name, :basePay, :hourly, :timeCard) do\n  def calculatePay(taxRate)\n    if (hourly) then\n      return calculateHourlyPayFor(taxRate)\n    end\n    return calculateSalariedFor(taxRate)\n  end\n\nprivate\n  def calculateHourlyPayFor(taxRate)\n    return (basePay * timeCard) - (basePay * timeCard) * taxRate\n  end\n\n  def calculateSalariedFor(taxRate)\n    return basePay - (basePay * taxRate)\n  end\nend</code>\n        </deckgo-highlight-code>\n<ul>\n<li>직원을 지정하지 않아도 된다.</li>\n</ul>\n<deckgo-highlight-code language=\"ruby\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">Employee = Struct.new(:name, :basePay, :hourly, :timeCard) do\n  def monthlyBasePay()\n    if (hourly) then return 0 end\n    return basePay\n  end\nend</code>\n        </deckgo-highlight-code>\n<deckgo-highlight-code language=\"ruby\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">$employees = [\n    Employee.new(&quot;직원A&quot;, 400, false, 0),\n    Employee.new(&quot;직원B&quot;, 300, false, 0),\n    Employee.new(&quot;직원C&quot;, 200, false, 0),\n    Employee.new(&quot;아르바이트D&quot;, 1, true, 120),\n    Employee.new(&quot;아르바이트E&quot;, 1, true, 120),\n    Employee.new(&quot;아르바이트F&quot;, 1, true, 120),\n]</code>\n        </deckgo-highlight-code>\n<ul>\n<li>직원들의 인스턴스</li>\n</ul>\n<deckgo-highlight-code language=\"ruby\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">def calculatePay(name)\n  taxRate = getTaxRate()\n  for each in $employees\n    if (each.name === name) then employee = each; break end\n  end\n  pay = employee. calculatePay(taxRate)\n  puts(describeResult(name, pay))\nend</code>\n        </deckgo-highlight-code>\n<deckgo-highlight-code language=\"ruby\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">def sumOfBasePays()\n  result = 0\n  for each in $employees\n    result += each.monthlyBasePay()\n  end\n  puts(result)\nend</code>\n        </deckgo-highlight-code>\n<ul>\n<li>추상 데이터 타입은 사람들이 세상을 바라보는 방식에 좀 더 근접해지도록 추상화 수준을 향상시킨다.</li>\n<li>여전히 데이터와 기능을 분리해서 바라본다.</li>\n<li>추상 데이터 타입은 말 그대로 시스템의 상태를 저장할 데이터를 표현한다.</li>\n</ul>\n<p>추상 데이터 타입의 기본 의도는 프로그래밍 언어가 제공하는 타입처럼 동작하는 사용자 정의 타입을 추가할 수 있게 하는 것</p>\n<h2 id=\"-75-클래스\" style=\"position:relative;\"><a href=\"#-75-%ED%81%B4%EB%9E%98%EC%8A%A4\" aria-label=\" 75 클래스 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>📖 7.5 클래스</h2>\n<h3 id=\"-751-클래스는-추상-데이터-타입인가\" style=\"position:relative;\"><a href=\"#-751-%ED%81%B4%EB%9E%98%EC%8A%A4%EB%8A%94-%EC%B6%94%EC%83%81-%EB%8D%B0%EC%9D%B4%ED%84%B0-%ED%83%80%EC%9E%85%EC%9D%B8%EA%B0%80\" aria-label=\" 751 클래스는 추상 데이터 타입인가 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>🔖 7.5.1 클래스는 추상 데이터 타입인가?</h3>\n<p>클래스와 추상 데이터 타입 모두 데이터 추상화를 기반으로 시스템을 분해한다. 그러나 명확한 의미에서 추상 데이터 타입과 클래스는 동일하지 않다.</p>\n<ul>\n<li>클래스는 상속과 다형성을 지원(객체지향 프로그래밍)하지만 추상 데이터 타입은 지원하지 못한다(객체기반 프로그래밍).</li>\n</ul>\n<p>하나의 대표적인 타입이 다수의 세부적인 타입을 감추기 때문에 이를 타입 추상화라 부른다.</p>\n<ul>\n<li>Employee 타입은 직원 타입과 아르바이트 타입이 있다.</li>\n<li>타입 추상화를 기반으로 하는 대표적인 기법이 바로 추상 데이터 타입이다.</li>\n</ul>\n<p>추상 데이터 타입이 오퍼레이션을 기준으로 타입을 묶는 방법이라면 객체지향은 타입을 기준으로 오퍼레이션을 묶는다.</p>\n<ul>\n<li>클래스를 이용한 다형성은 절차에 대한 차이점을 감춘다.</li>\n<li>객체 지향은 <strong>절차 추상화</strong>다.</li>\n</ul>\n<h3 id=\"-752-추상-데이터-타입에서-클래스로-변경하기\" style=\"position:relative;\"><a href=\"#-752-%EC%B6%94%EC%83%81-%EB%8D%B0%EC%9D%B4%ED%84%B0-%ED%83%80%EC%9E%85%EC%97%90%EC%84%9C-%ED%81%B4%EB%9E%98%EC%8A%A4%EB%A1%9C-%EB%B3%80%EA%B2%BD%ED%95%98%EA%B8%B0\" aria-label=\" 752 추상 데이터 타입에서 클래스로 변경하기 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>🔖 7.5.2 추상 데이터 타입에서 클래스로 변경하기</h3>\n<deckgo-highlight-code language=\"ruby\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">class Employee\n  attr_reader :name, :basePay\n\n  def initialize(name, basePay)\n    @name = name\n    @basePay = basePay\n  end\n\n  def calculatePay(taxRate)\n    raise NotImplementedError\n  end\n\n  def monthlyBasePay()\n    raise NotImplementedError\n  end\nend</code>\n        </deckgo-highlight-code>\n<ul>\n<li>java 기준으로 보면 <code>Employee</code> class는 추상 클래스, <code>calculatePay</code>, <code>monthlyBasePay</code>는 추상 메서드이다.</li>\n</ul>\n<deckgo-highlight-code language=\"ruby\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">class SalariedEmployee &lt; Employee\n  def initialize(name, basePay)\n    super(name, basePay)\n  end\n\n  def calculatePay(taxRate)\n    return basePay - (basePay * taxRate)\n  end\n\n  def monthlyBasePay()\n    return basePay\n  end\nend</code>\n        </deckgo-highlight-code>\n<deckgo-highlight-code language=\"ruby\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">class HourlyEmployee &lt; Employee\n  attr_reader :timeCard\n  def initialize(name, basePay, timeCard)\n    super(name, basePay)\n    @timeCard = timeCard\n  end\n\n  def calculatePay(taxRate)\n    return (basePay * timeCard) - (basePay * timeCard) * taxRate\n  end\n\n  def monthlyBasePay()\n    return 0\n  end\nend</code>\n        </deckgo-highlight-code>\n<deckgo-highlight-code language=\"ruby\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">$employees = [\n    SalariedEmployee.new(&quot;직원A&quot;, 400),\n    SalariedEmployee.new(&quot;직원B&quot;, 300),\n    SalariedEmployee.new(&quot;직원C&quot;, 200),\n    HourlyEmployee.new(&quot;아르바이트D&quot;, 1, 120),\n    HourlyEmployee.new(&quot;아르바이트E&quot;, 1, 120),\n    HourlyEmployee.new(&quot;아르바이트F&quot;, 1, 120),\n]</code>\n        </deckgo-highlight-code>\n<deckgo-highlight-code language=\"ruby\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">def sumOfBasePays()\n  result = 0\n  for each in $employees\n    result += each.monthlyBasePay()\n  end\n  puts(result)\nend</code>\n        </deckgo-highlight-code>\n<ul>\n<li>메시지를 수신한 객체는 자신의 클래스에 구현된 메서드를 이용해 적절하게 반응할 수 있다.</li>\n</ul>\n<h3 id=\"-753-변경을-기준으로-선택하라\" style=\"position:relative;\"><a href=\"#-753-%EB%B3%80%EA%B2%BD%EC%9D%84-%EA%B8%B0%EC%A4%80%EC%9C%BC%EB%A1%9C-%EC%84%A0%ED%83%9D%ED%95%98%EB%9D%BC\" aria-label=\" 753 변경을 기준으로 선택하라 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>🔖 7.5.3 변경을 기준으로 선택하라</h3>\n<p>비록 클래스를 사용하고 있더라도 타입을 기준으로 절차를 추상화하지 않았다면 그것은 객체지향 분해가 아니다.</p>\n<ul>\n<li>클래스가 추상 데이터 타입의 개념을 다르는지를 확인할 수 있는 가장 간단한 방법은 클래스 내부에 인스턴스의 타입을 표현하는 변수가 있는지를 살펴보는 것</li>\n<li>객체지향에서는 타입 변수를 이용한 조건문을 다형성으로 대체</li>\n</ul>\n<p>개방-폐쇄 원칙(Open-Closed Principle, OCP)</p>\n<ul>\n<li>기존 코드에 아무런 영향도 미치지 않고 새로운 객체 유형과 행위를 추가할 수 있는 객체지향의 특성</li>\n<li>대부분의 객체지향 서적에서는 추상 데이터 타입을 기반으로 애플리케이션을 설계하는 방식을 잘못된 것으로 설명한다.\n<ul>\n<li>설계에 요구되는 변경의 압력이 '타입 추가'에 관한 것인지, 아니면 '오퍼레이션 추가'에 관한 것인지에 따라 달라진다.</li>\n<li>새로운 타입을 빈번하게 추가해야 한다면 객체지향의 클래스 구조가 더 유용</li>\n<li>새로운 오퍼레이션을 빈번하게 추가해야 한다면 추상 데이터 타입이 더 유용</li>\n</ul>\n</li>\n</ul>\n<h3 id=\"-754-협력이-중요하다\" style=\"position:relative;\"><a href=\"#-754-%ED%98%91%EB%A0%A5%EC%9D%B4-%EC%A4%91%EC%9A%94%ED%95%98%EB%8B%A4\" aria-label=\" 754 협력이 중요하다 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>🔖 7.5.4 협력이 중요하다</h3>\n<p>협력이라는 문맥을 고려하지 않고 객체를 고립시킨 채 오퍼레이션의 구현 방식을 타입별로 분배하는 것은 올바른 접근법이 아니다.</p>\n<p>객체가 참여할 협력을 결정하고 협력에 필요한 책임을 수행하기 위해 어떤 객체가 필요한지에 관해 고민하라.</p>\n<ul>\n<li>그 책임을 다양한 방식으로 수행해야 할 때만 타입 계층 안에 각 절차를 추상화하라.</li>\n<li>타입 계층과 다형성은 협력이라는 문맥 안에서 책임을 수행하는 방법에 관해 고민한 결과물이어야 하며 그 자체가 목적이 되어서는 안 된다.</li>\n</ul>","excerpt":"사람의 기억은 단기 기억과 장기 기억으로 분류할 수 있다. 실제로 문제를 해결하기 위해 사용하는 저장소는 장기 기억이 아니라 단기 기억이다. 인지 과부하(cognitive overload): 문제 해결에 필요한 요소의 수가 단기 기억의 용량을 초과하는 순간 문제 해결 능력이 급격하게 떨어지는 것 인지 과부하를 줄이는 가장 좋은 방법은 단기 기억 안에 보관할 정보의 양을 조절하는 것 추상화: 불필요한 정보를 제거하고 현재의 문제 해결에 필요한 핵심만 남기는 작업 분해(decomposition…","fields":{"slug":"/backend/object/chapter7/객체_분해/"},"frontmatter":{"title":"Object - 7장 객체 분해","thumbnail":{"childImageSharp":{"fluid":{"src":"/static/dd0e1990925e942697544e2bdcd9332e/9b73b/object.png"}}},"draft":false,"category":"Back-End","tags":["java"],"date":"October 08, 2023"}},"node":{"id":"1d60c7d6-474c-5c82-af8d-2050f2560f1c","html":"<blockquote>\n<p>애플리케이션은 클래스로 구성되지만 메시지를 통해 정의된다는 사실을 기억하라.</p>\n</blockquote>\n<h2 id=\"-61-협력과-메시지\" style=\"position:relative;\"><a href=\"#-61-%ED%98%91%EB%A0%A5%EA%B3%BC-%EB%A9%94%EC%8B%9C%EC%A7%80\" aria-label=\" 61 협력과 메시지 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>📖 6.1 협력과 메시지</h2>\n<h3 id=\"-611-클라이언트-서버-모델\" style=\"position:relative;\"><a href=\"#-611-%ED%81%B4%EB%9D%BC%EC%9D%B4%EC%96%B8%ED%8A%B8-%EC%84%9C%EB%B2%84-%EB%AA%A8%EB%8D%B8\" aria-label=\" 611 클라이언트 서버 모델 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>🔖 6.1.1 클라이언트-서버 모델</h3>\n<p>메시지는 객체 사이의 협력을 가능하게 하는 매개체다. 두 객체 사이의 협력 관계를 설명하기 위해 사용하는 전통적인 메타포는 <strong>클라이언트-서버 모델</strong> 이다.</p>\n<ul>\n<li><code>Movie</code>와 같이 객체는 협력에 참여하는 동안 클라이언트와 서버의 역할을 동시에 수행하는 것이 일반적</li>\n<li>협력의 관점에서 객체는 두 가지 종류의 메시지 집합으로 구성\n<ul>\n<li>객체가 수신하는 메시지의 집합</li>\n<li>외부의 객체에게 전송하는 메시지의 집합</li>\n</ul>\n</li>\n<li>두 객체 사이의 협력을 가능하게 해주는 매개체가 메시지</li>\n</ul>\n<h3 id=\"-612-메시지와-메시지-전송\" style=\"position:relative;\"><a href=\"#-612-%EB%A9%94%EC%8B%9C%EC%A7%80%EC%99%80-%EB%A9%94%EC%8B%9C%EC%A7%80-%EC%A0%84%EC%86%A1\" aria-label=\" 612 메시지와 메시지 전송 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>🔖 6.1.2 메시지와 메시지 전송</h3>\n<ul>\n<li>메시지는 객체들이 협력하기 위해 사용할 수 있는 유일한 의사소통 수단</li>\n<li><strong>메시지 전송(메시지 패싱)</strong>: 한 객체가 다른 객체에게 도움을 요청하는 것</li>\n<li><strong>메시지 전송자(클라이언트)</strong>: 메시지를 전송하는 객체</li>\n<li><strong>메시지 수신자(서버)</strong>: 메시지를 수신하는 객체</li>\n<li>메시지는 <strong>오퍼레이션명</strong>과 <strong>인자</strong>로 구성\n<ul>\n<li>메시지 전송은 여기에 <strong>메시지 수신자</strong>를 추가한 것</li>\n</ul>\n</li>\n</ul>\n<h3 id=\"-613-메시지와-메서드\" style=\"position:relative;\"><a href=\"#-613-%EB%A9%94%EC%8B%9C%EC%A7%80%EC%99%80-%EB%A9%94%EC%84%9C%EB%93%9C\" aria-label=\" 613 메시지와 메서드 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>🔖 6.1.3 메시지와 메서드</h3>\n<ul>\n<li>메시지를 수신했을 때 실제로 실행되는 함수 또는 프로시저를 <strong>메서드</strong>라고 부른다.\n<ul>\n<li>동일한 메시지를 전송하더라도 객체의 타입에 따라 실행되는 메서드가 달라질 수 있다.</li>\n<li>객체는 메시지와 메서드라는 두 가지 서로 다른 개념을 실행 시점에 연결해야 하기 때문에 컴파일 시점과 실행 시점의 의미가 달라질 수 있다.</li>\n</ul>\n</li>\n<li>메시지와 메서드의 구분은 메시지 전송자와 메시지 수신자가 느슨하게 결합될 수 있게 한다.</li>\n<li>실행 시점에 메시지와 메서드를 바인딩하는 메커니즘은 두 객체 사이의 결합도를 낮춤으로써 유연하고 확장 가능한 코드를 작성할 수 있게 만든다.</li>\n</ul>\n<h3 id=\"-614-퍼블릭-인터페이스와-오퍼레이션\" style=\"position:relative;\"><a href=\"#-614-%ED%8D%BC%EB%B8%94%EB%A6%AD-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4%EC%99%80-%EC%98%A4%ED%8D%BC%EB%A0%88%EC%9D%B4%EC%85%98\" aria-label=\" 614 퍼블릭 인터페이스와 오퍼레이션 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>🔖 6.1.4 퍼블릭 인터페이스와 오퍼레이션</h3>\n<ul>\n<li><strong>퍼블릭 인터페이스</strong>: 객체가 의사소통을 위해 외부에 공개하는 메시지의 집합</li>\n<li><strong>오퍼레이션</strong>: 프로그래밍 언어의 관점에서 퍼블릭 인터페이스에 포함된 메시지\n<ul>\n<li>수행 가능한 어떤 행동에 대한 추상화</li>\n<li>UML의 관점에서 오퍼레이션이란 실행하기 위해 객체가 호출될 수 있는 변환이나 정의에 관한 명세</li>\n</ul>\n</li>\n</ul>\n<h3 id=\"-615-시그니처\" style=\"position:relative;\"><a href=\"#-615-%EC%8B%9C%EA%B7%B8%EB%8B%88%EC%B2%98\" aria-label=\" 615 시그니처 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>🔖 6.1.5 시그니처</h3>\n<ul>\n<li><strong>시그니처</strong>: 오퍼레이션(또는 메서드)의 이름과 파라미터 목록을 합친 것\n<ul>\n<li>오퍼레이션은 실행 코드 없이 시그니처만을 정의한 것</li>\n<li>메서드는 이 시그니처에 구현을 더한 것</li>\n</ul>\n</li>\n</ul>\n<h2 id=\"-62-인터페이스와-설계-품질\" style=\"position:relative;\"><a href=\"#-62-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4%EC%99%80-%EC%84%A4%EA%B3%84-%ED%92%88%EC%A7%88\" aria-label=\" 62 인터페이스와 설계 품질 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>📖 6.2 인터페이스와 설계 품질</h2>\n<p>좋은 인터페이스는 <strong>최소한의 인터페이스</strong>와 <strong>추상적인 인터페이스</strong>라는 조건을 만족해야 한다.</p>\n<p>퍼블릭 인터페이스의 품질에 영향을 미치는 원칙과 기법</p>\n<ul>\n<li>디미터 법칙</li>\n<li>묻지 말고 시켜라</li>\n<li>의도를 드러내는 인터페이스</li>\n<li>명령-쿼리 분리</li>\n</ul>\n<h3 id=\"-621-디미터-법칙\" style=\"position:relative;\"><a href=\"#-621-%EB%94%94%EB%AF%B8%ED%84%B0-%EB%B2%95%EC%B9%99\" aria-label=\" 621 디미터 법칙 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>🔖 6.2.1 디미터 법칙</h3>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">public class ReservationAgency {\n\n    public Reservation reserve(Screening screening, Customer customer, int audienceCount) {\n        Movie movie = screening.getMovie();\n\n        boolean discountable = false;\n        for (DiscountCondition condition : movie.getDiscountConditions()) {\n            if (condition.getType() == DiscountConditionType.PERIOD) {\n                discountable = screening.getWhenScreened().getDayOfWeek().equals(condition.getDayOfWeek()) &amp;&amp;\n                        !condition.getStartTime().isAfter(screening.getWhenScreened().toLocalTime()) &amp;&amp;\n                        !condition.getEndTime().isBefore(screening.getWhenScreened().toLocalTime());\n            } else {\n                discountable = condition.getSequence() == screening.getSequence();\n            }\n\n            if (discountable) {\n                break;\n            }\n        }\n\n        Money fee;\n        if (discountable) {\n            Money discountAmount = Money.ZERO;\n            switch (movie.getMovieType()) {\n                case AMOUNT_DISCOUNT -&gt; discountAmount = movie.getDiscountAmount();\n                case PERCENT_DISCOUNT -&gt; discountAmount = movie.getFee().times(movie.getDiscountPercent());\n                case NONE_DISCOUNT -&gt; discountAmount = Money.ZERO;\n            }\n\n            fee = movie.getFee().minus(discountAmount);\n        } else {\n            fee = movie.getFee();\n        }\n\n        return new Reservation(customer, screening, fee, audienceCount);\n    }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>위 코드의 가장 큰 단점은 <code>ReservationAgency</code>와 인자로 전달된 <code>Screening</code> 사이의 결합도가 너무 높은 것이다.</li>\n<li>이처럼 협력하는 객체의 내부 구조에 대한 결합으로 인해 발생하는 설계 문제를 해결하기 위해 제안된 원칙이 <strong>디미터 법칙</strong></li>\n</ul>\n<blockquote>\n<p>낯선 자에게 말하지 말라</p>\n<p>오직 인접한 이웃하고만 말하라</p>\n</blockquote>\n<p>디미터 법칙을 따르기 위해서는 클래스가 특정한 조건을 만족하는 대상에게만 메시지를 전송하도록 프로그래밍</p>\n<ul>\n<li>모든 클래스 C와 C에 구현된 모든 메서드 M에 대해서, M이 메시지를 전송할 수 있는 모든 객체는 아래 클래스의 인스턴스여야 한다.\n<ul>\n<li>M의 인자로 전달된 클래스(C 자체를 포함)</li>\n<li>C의 인스턴스 변수의 클래스</li>\n</ul>\n</li>\n<li>M에 의해 생성된 객체나 M이 호출하는 메서드에 의해 생성된 객체, 전역 변수로 선언된 객체는 모두 M의 인자로 간주</li>\n</ul>\n<p>즉, 클래스 내부의 메서드가 아래 조건을 만족하는 인스턴스에만 메시지를 전송하도록 해야 한다.</p>\n<ul>\n<li>this 객체</li>\n<li>메서드의 매개변수</li>\n<li>this의 속성</li>\n<li>this의 속성인 컬렉션의 요소</li>\n<li>메서드 내에서 생성된 지역 객체</li>\n</ul>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">public class ReservationAgency {\n\n    public Reservation reserve(Screening screening, Customer customer, int audienceCount) {\n        Money fee = screening.calculateFee(audienceCount);\n        return new Reservation(customer, screening, fee, audienceCount);\n    }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>디미터 법칙을 따른 결과 결합도가 개선되었다.</li>\n<li>디미터 법칙을 따르면 <strong>부끄럼타는 코드(shy code)</strong> 를 작성할 수 있다.\n<ul>\n<li>불필요한 어떤 것도 다른 객체에게 보여주지 않음</li>\n<li>다른 객체의 구현에 의존하지 않는 코드</li>\n</ul>\n</li>\n</ul>\n<p><code>screening.getMovie().getDiscountConditions();</code></p>\n<ul>\n<li>위 코드는 디미터 법칙을 위반한 코드다.</li>\n<li>메시지 전송자가 수신자의 내부 구조에 대해 물어보고 반환받은 요소에 대해 연쇄적으로 메시지를 전송</li>\n<li><strong>기차 충돌(train wreck)</strong>\n<ul>\n<li>여러 대의 기차가 한 줄로 늘어서 충돌한 것처럼 보이기 때문</li>\n<li>클래스의 내부 구현이 외부로 노출됐을 때 나타나는 전형적인 형태</li>\n</ul>\n</li>\n<li>디미터 법칙을 적용한 코드는 <code>screening.calculateFee(audienceCount);</code></li>\n</ul>\n<p>무비판적으로 디미터 법칙을 수용하면 퍼블릭 인터페이스 관점에서 객체의 응집도가 낮아질 수도 있다.</p>\n<h3 id=\"-622-묻지-말고-시켜라\" style=\"position:relative;\"><a href=\"#-622-%EB%AC%BB%EC%A7%80-%EB%A7%90%EA%B3%A0-%EC%8B%9C%EC%BC%9C%EB%9D%BC\" aria-label=\" 622 묻지 말고 시켜라 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>🔖 6.2.2 묻지 말고 시켜라</h3>\n<p>메시지 전송자는 메시지 수신자의 상태를 기반으로 결정을 내린 후 메시지 수신자의 상태를 바꿔서는 안된다.</p>\n<ul>\n<li>묻지 말고 시켜라 원칙을 따르면 밀접하게 연관된 정보와 행동을 함께 가지는 객체를 만들 수 있다.</li>\n<li>상태를 묻는 오퍼레이션을 행동을 요청하는 오퍼레이션으로 대체함으로써 인터페이스를 향상시켜라</li>\n<li>하지만 단순하게 객체에게 묻지 않고 시킨다고 해서 모든 문제가 해결되는 것은 아니다.\n<ul>\n<li>훌륭한 인터페이스를 수확하기 위해서는 객체가 어떻게 작업을 수행하는지를 노출해서는 안 된다.</li>\n</ul>\n</li>\n</ul>\n<h3 id=\"-623-의도를-드러내는-인터페이스\" style=\"position:relative;\"><a href=\"#-623-%EC%9D%98%EB%8F%84%EB%A5%BC-%EB%93%9C%EB%9F%AC%EB%82%B4%EB%8A%94-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4\" aria-label=\" 623 의도를 드러내는 인터페이스 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>🔖 6.2.3 의도를 드러내는 인터페이스</h3>\n<p>메서드를 명명하는 방법</p>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">public class PeriodCondition {\n    public boolean isSatisfiedByPeriod(Screening screening) {\n        ...\n    }\n}\n\npublic class SequenceCondition {\n    public boolean isSatisfiedBySequence(Screening screening) {\n        ...\n    }\n}</code>\n        </deckgo-highlight-code>\n<ol>\n<li>\n<p>메서드가 작업을 어떻게 수행하는지를 나타내도록 이름 짓는 것</p>\n<ul>\n<li>메서드의 이름은 내부의 구현 방법을 드러낸다.</li>\n<li>메서드에 대해 제대로 커뮤니케이션하지 못한다.</li>\n<li>메서드 수준에서 캡슐화를 위반한다.\n<ul>\n<li>클라이언트로 하여금 협력하는 객체의 종류를 알도록 강요</li>\n</ul>\n</li>\n</ul>\n</li>\n<li>\n<p>'어떻게'가 아니라 '무엇'을 하는지를 드러내는 것</p>\n<ul>\n<li>어떻게 수행하는지를 드러내는 이름이란 메서드의 내부 구현을 설명하는 이름</li>\n<li>무엇을 하는지를 드러내도록 메서드의 이름을 짓기 위해서는 객체가 협력 안에서 수행해야 하는 책임에 관해 고민</li>\n</ul>\n</li>\n</ol>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">public class PeriodCondition {\n    public boolean isSatisfiedBy(Screening screening) {\n        ...\n    }\n}\n\npublic class SequenceCondition {\n    public boolean isSatisfiedBy(Screening screening) {\n        ...\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\">public interface DiscountCondition {\n    boolean isSatisfiedBy(Screening screening);\n}</code>\n        </deckgo-highlight-code>\n<p>이와 같은 메서드 명명 방법을 <strong>의도를 드러내는 선택자(Intention Revealing Selector)</strong> 라고 부른다.</p>\n<ul>\n<li>에릭 에반스는 의도를 드러내는 선택자를 인터페이스 레벨로 확장한 **의도를 드러내는 인터페이스(Intention Revealing Interface)**를 제시\n<ul>\n<li>구현과 관련된 모든 정보를 캡슐화하고 객체의 퍼블릭 인터페이스에는 협력과 관련된 의도만을 표현</li>\n</ul>\n</li>\n</ul>\n<h3 id=\"-624-함께-모으기\" style=\"position:relative;\"><a href=\"#-624-%ED%95%A8%EA%BB%98-%EB%AA%A8%EC%9C%BC%EA%B8%B0\" aria-label=\" 624 함께 모으기 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>🔖 6.2.4 함께 모으기</h3>\n<h4 id=\"️-6241-디미터-법칙을-위반하는-티켓-판매-도메인\" style=\"position:relative;\"><a href=\"#%EF%B8%8F-6241-%EB%94%94%EB%AF%B8%ED%84%B0-%EB%B2%95%EC%B9%99%EC%9D%84-%EC%9C%84%EB%B0%98%ED%95%98%EB%8A%94-%ED%8B%B0%EC%BC%93-%ED%8C%90%EB%A7%A4-%EB%8F%84%EB%A9%94%EC%9D%B8\" aria-label=\"️ 6241 디미터 법칙을 위반하는 티켓 판매 도메인 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>🛠️ 6.2.4.1 디미터 법칙을 위반하는 티켓 판매 도메인</h4>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">@AllArgsConstructor\npublic class Theater {\n\n    private TicketSeller ticketSeller;\n\n    // 관람객 입장\n    public void enter(Audience audience) {\n        if (audience.getBag().hasInvitation()) {\n            Ticket ticket = ticketSeller.getTicketOffice().getTicket();\n            audience.getBag().setTicket(ticket);\n            return;\n        }\n        Ticket ticket = ticketSeller.getTicketOffice().getTicket();\n        audience.getBag().minusAmount(ticket.getFee());\n        ticketSeller.getTicketOffice().plusAmount(ticket.getFee());\n        audience.getBag().setTicket(ticket);\n    }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>디미터 법칙을 위반한 전형적인 코드</li>\n<li><strong>인터페이스와 구현의 분리 원칙</strong> 위반</li>\n<li>불안정하고, 사용하기 힘들고, 클라이언트에게 구현을 노출</li>\n</ul>\n<h4 id=\"️-6242-묻지-말고-시켜라\" style=\"position:relative;\"><a href=\"#%EF%B8%8F-6242-%EB%AC%BB%EC%A7%80-%EB%A7%90%EA%B3%A0-%EC%8B%9C%EC%BC%9C%EB%9D%BC\" aria-label=\"️ 6242 묻지 말고 시켜라 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>🛠️ 6.2.4.2 묻지 말고 시켜라</h4>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">@AllArgsConstructor\npublic class TicketSeller {\n\n    private TicketOffice ticketOffice;\n\n    public void setTicket(Audience audience) {\n        if (audience.getBag().hasInvitation()) {\n            Ticket ticket = ticketOffice.getTicket();\n            audience.getBag().setTicket(ticket);\n            return;\n        }\n        Ticket ticket = ticketOffice.getTicket();\n        audience.getBag().minusAmount(ticket.getFee());\n        ticketOffice.plusAmount(ticket.getFee());\n        audience.getBag().setTicket(ticket);\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 Theater {\n\n    private TicketSeller ticketSeller;\n\n    public void enter(Audience audience) {\n        ticketSeller.setTicket(audience);\n    }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li><code>Theater</code>는 <code>TicketSeller</code>와 <code>Audience</code>의 내부 구조에 관해 묻지 말고 원하는 작업을 시켜야 한다.</li>\n</ul>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">@AllArgsConstructor\npublic class Audience {\n\n    private Bag bag;\n\n    public Long setTicket(Ticket ticket) {\n        if (bag.hasInvitation()) {\n            bag.setTicket(ticket);\n            return 0L;\n        }\n        bag.setTicket(ticket);\n        bag.minusAmount(ticket.getFee());\n        return ticket.getFee();\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 TicketSeller {\n\n    private TicketOffice ticketOffice;\n\n    public void setTicket(Audience audience) {\n        ticketOffice.plusAmount(audience.setTicket(ticketOffice.getTicket()));\n    }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li><code>TicketSeller</code>는 속성으로 포함되고 있는 <code>TicketOffice</code>의 인스턴스와 인자로 전달된 <code>Audience</code>에게만 메시지를 전송한다.</li>\n<li><code>Audience</code>는 <code>hasInvitation</code> 메서드를 이용해 초대권을 가지고 있는지를 묻는다. 즉, 디미터 법칙을 위반한다.</li>\n</ul>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">public class Bag {\n\n    private Long amount;\n    private Invitation invitation;\n    private Ticket ticket;\n\n    public Long setTicket(Ticket ticket) {\n        if (hasInvitation()) {\n            setTicket(ticket);\n            return 0L;\n        }\n        setTicket(ticket);\n        minusAmount(ticket.getFee());\n        return ticket.getFee();\n    }\n\n    public boolean hasInvitation() {\n        return invitation != null;\n    }\n\n    public void minusAmount(Long amount) {\n        this.amount -= amount;\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 Audience {\n\n    private Bag bag;\n\n    public Long setTicket(Ticket ticket) {\n        return bag.setTicket(ticket);\n    }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li><code>Audience</code>는 자율적인 존재가 되었다.</li>\n</ul>\n<p>디미터 법칙과 묻지 말고 시켜라 스타일을 따르면 자연스럽게 자율적인 객체로 구성된 유연한 협력을 얻게 된다.</p>\n<h4 id=\"️-6243-인터페이스에-의도를-드러내자\" style=\"position:relative;\"><a href=\"#%EF%B8%8F-6243-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4%EC%97%90-%EC%9D%98%EB%8F%84%EB%A5%BC-%EB%93%9C%EB%9F%AC%EB%82%B4%EC%9E%90\" aria-label=\"️ 6243 인터페이스에 의도를 드러내자 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>🛠️ 6.2.4.3 인터페이스에 의도를 드러내자</h4>\n<p>위 코드들의 <code>setTicket</code> 메서드는 미묘하게 다른 의미를 가지고 있다❗️</p>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">public class TicketSeller {\n    public void sellTo(Audience audience) {\n        ...\n    }\n}\n\npublic class Audience {\n    public Long buy(Ticket ticket) {\n        ...\n    }\n}\n\npublic class Bag {\n    public Long hold(Ticket ticket) {\n        ...\n    }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>위 메서드 명칭은 클라이언트가 객체에게 무엇을 원하는지를 명확하게 표현</li>\n</ul>\n<h2 id=\"-63-원칙의-함정\" style=\"position:relative;\"><a href=\"#-63-%EC%9B%90%EC%B9%99%EC%9D%98-%ED%95%A8%EC%A0%95\" aria-label=\" 63 원칙의 함정 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>📖 6.3 원칙의 함정</h2>\n<p>원칙이 현재 상황에 부적합하다고 판단된다면 과감하게 원칙을 무시하라.</p>\n<h3 id=\"-631-디미터-법칙은-하나의-도트를-강제하는-규칙이-아니다\" style=\"position:relative;\"><a href=\"#-631-%EB%94%94%EB%AF%B8%ED%84%B0-%EB%B2%95%EC%B9%99%EC%9D%80-%ED%95%98%EB%82%98%EC%9D%98-%EB%8F%84%ED%8A%B8%EB%A5%BC-%EA%B0%95%EC%A0%9C%ED%95%98%EB%8A%94-%EA%B7%9C%EC%B9%99%EC%9D%B4-%EC%95%84%EB%8B%88%EB%8B%A4\" aria-label=\" 631 디미터 법칙은 하나의 도트를 강제하는 규칙이 아니다 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>🔖 6.3.1 디미터 법칙은 하나의 도트(.)를 강제하는 규칙이 아니다</h3>\n<p><code>IntStream.of(1, 15, 20, 3, 9).filter(x -> x > 10).distinct().count();</code></p>\n<ul>\n<li>위 코드는 동일한 클래스의 인스턴스를 반환하므로 디미터 법칙을 위반하지 않는다.</li>\n<li>기차 충돌처럼 보이는 코드라도 객체의 내부 구현에 대한 어떤 정보도 외부로 노출하지 않는다면 그것은 디미터 법칙을 준수한 것이다.</li>\n</ul>\n<h3 id=\"-632-결합도와-응집도의-충돌\" style=\"position:relative;\"><a href=\"#-632-%EA%B2%B0%ED%95%A9%EB%8F%84%EC%99%80-%EC%9D%91%EC%A7%91%EB%8F%84%EC%9D%98-%EC%B6%A9%EB%8F%8C\" aria-label=\" 632 결합도와 응집도의 충돌 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>🔖 6.3.2 결합도와 응집도의 충돌</h3>\n<ul>\n<li>모든 상황에서 맹목적으로 위임 메서드를 추가하면 같은 퍼블릭 인터페이스 안에 어울리지 않는 오퍼레이션들이 공존하게 된다. 결과적으로 응집도 ⬇️</li>\n<li>가끔씩은 묻는 것 외에는 다른 방법이 존재하지 않는 경우도 있다.\n<ul>\n<li>컬렉션에 포함된 객체들을 처리하는 유일한 방법은 객체에게 물어보는 것</li>\n<li>묻는 대상이 자료 구조라면 당연히 내부를 노출해야 하므로 디미터 법칙을 적용할 필요가 없다.</li>\n</ul>\n</li>\n</ul>\n<p>설계는 trade-off의 산물이다. 즉, 경우에 따라 다르다</p>\n<p>원칙이 적절한 상황과 부적절한 상황을 판단할 수 있는 안목을 길러라❗️</p>\n<h2 id=\"-64-명령-쿼리-분리-원칙\" style=\"position:relative;\"><a href=\"#-64-%EB%AA%85%EB%A0%B9-%EC%BF%BC%EB%A6%AC-%EB%B6%84%EB%A6%AC-%EC%9B%90%EC%B9%99\" aria-label=\" 64 명령 쿼리 분리 원칙 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>📖 6.4 명령-쿼리 분리 원칙</h2>\n<ul>\n<li><strong>루틴(routine)</strong>: 어떤 절차를 묶어 호출 가능하도록 이름을 부여한 기능 모듈\n<ul>\n<li>**프로시저(procedure)**와 **함수(function)**로 구분</li>\n</ul>\n</li>\n<li>프로시저(procedure): 정해진 절차에 따라 내부의 상태를 변경하는 루틴의 한 종류\n<ul>\n<li>부수효과를 발생시킬 수 있지만 값을 반환할 수 없다.</li>\n<li>객체의 인터페이스 측면에서 **명령(Command)**와 동일</li>\n</ul>\n</li>\n<li>함수(function): 어떤 절차에 따라 필요한 값을 계산해서 반환하는 루틴의 한 종류\n<ul>\n<li>값을 반환할 수 있지만 부수효과를 발생시킬 수 없다.</li>\n<li>객체의 인터페이스 측면에서 **쿼리(Query)**와 동일</li>\n</ul>\n</li>\n</ul>\n<p>명령-쿼리 분리 원칙의 요지는 오퍼레이션은 부수효과를 발생시키는 명령이거나 부수효과를 발생시키지 않는 쿼리 중 하나여야 한다는 것</p>\n<ul>\n<li>객체의 상태를 변경하는 명령은 반환값을 가질 수 없다.</li>\n<li>객체의 정보를 반환하는 쿼리는 상태를 변경할 수 없다.</li>\n<li>\"질문이 답변을 수정해서는 안 된다\"</li>\n</ul>\n<h3 id=\"-641-반복-일정의-명령과-쿼리-분리하기\" style=\"position:relative;\"><a href=\"#-641-%EB%B0%98%EB%B3%B5-%EC%9D%BC%EC%A0%95%EC%9D%98-%EB%AA%85%EB%A0%B9%EA%B3%BC-%EC%BF%BC%EB%A6%AC-%EB%B6%84%EB%A6%AC%ED%95%98%EA%B8%B0\" aria-label=\" 641 반복 일정의 명령과 쿼리 분리하기 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>🔖 6.4.1 반복 일정의 명령과 쿼리 분리하기</h3>\n<p>예시 도메인의 용어 정리</p>\n<ul>\n<li>이벤트: 특정 일자에 실제로 발생하는 사건</li>\n<li>반복 일정: 일주일 단위로 돌아오는 특정 시간 간격에 발생하는 사건 전체를 포괄적으로 지칭</li>\n</ul>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">@AllArgsConstructor\npublic class Event {\n\n    private String subject;\n    private LocalDateTime from;\n    private Duration duration;\n}</code>\n        </deckgo-highlight-code>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">@AllArgsConstructor\npublic class RecurringSchedule {\n\n    private String subject;\n\n    @Getter\n    private DayOfWeek dayOfWeek;\n\n    @Getter\n    private LocalTime from;\n\n    @Getter\n    private Duration duration;\n}</code>\n        </deckgo-highlight-code>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">RecurringSchedule schedule = new RecurringSchedule(&quot;회의&quot;, DayOfWeek.WEDNESDAY, LocalTime.of(10, 30), Duration.ofMinutes(30));\nEvent meeting = new Event(&quot;회의&quot;, LocalDateTime.of(2019, 5, 8, 10, 30), Duration.ofMinutes(30));\n\nassert meeting.isSatisfied(schedule);</code>\n        </deckgo-highlight-code>\n<ul>\n<li>위 코드는 당연히 true를 반환한다.</li>\n</ul>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">RecurringSchedule schedule = new RecurringSchedule(&quot;회의&quot;, DayOfWeek.WEDNESDAY, LocalTime.of(10, 30), Duration.ofMinutes(30));\nEvent meeting = new Event(&quot;회의&quot;, LocalDateTime.of(2019, 5, 9, 10, 30), Duration.ofMinutes(30));\n\nassert !meeting.isSatisfied(schedule);\nassert meeting.isSatisfied(schedule);</code>\n        </deckgo-highlight-code>\n<ul>\n<li>5/9는 목요일이므로 <code>false</code>를 반환한다. 하지만 다시 한번 더 실행시키면 <code>true</code>를 반환한다.</li>\n</ul>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">@AllArgsConstructor\npublic class Event {\n\n    private String subject;\n    private LocalDateTime from;\n    private Duration duration;\n\n    public boolean isSatisfied(RecurringSchedule schedule) {\n        if (from.getDayOfWeek() != schedule.getDayOfWeek() ||\n                !from.toLocalTime().equals(schedule.getFrom()) ||\n                !duration.equals(schedule.getDuration())) {\n            reschedule(schedule);\n            return false;\n        }\n        return true;\n    }\n\n    private void reschedule(RecurringSchedule schedule) {\n        from = LocalDateTime.of(from.toLocalDate().plusDays(daysDistance(schedule)), schedule.getFrom());\n        duration = schedule.getDuration();\n    }\n\n    private long daysDistance(RecurringSchedule schedule) {\n        return schedule.getDayOfWeek().getValue() - from.getDayOfWeek().getValue();\n    }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li><code>isSatisfied</code>가 명령과 쿼리의 두 가지 역할을 동시에 수행하고 있었기 때문에 발생한 문제</li>\n<li>명령과 쿼리를 뒤섞으면 실행 결과를 예측하기가 어려워질 수 있다.</li>\n</ul>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">@AllArgsConstructor\npublic class Event {\n\n    private String subject;\n    private LocalDateTime from;\n    private Duration duration;\n\n    public boolean isSatisfied(RecurringSchedule schedule) {\n        return from.getDayOfWeek() == schedule.getDayOfWeek() &amp;&amp;\n                from.toLocalTime().equals(schedule.getFrom()) &amp;&amp;\n                duration.equals(schedule.getDuration());\n    }\n\n    public void reschedule(RecurringSchedule schedule) {\n        from = LocalDateTime.of(from.toLocalDate().plusDays(daysDistance(schedule)), schedule.getFrom());\n        duration = schedule.getDuration();\n    }\n\n    private long daysDistance(RecurringSchedule schedule) {\n        return schedule.getDayOfWeek().getValue() - from.getDayOfWeek().getValue();\n    }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>이렇게 분리가 되어 <code>reschedule</code> 메서드 호출 여부를 <code>Event</code>를 사용하는 쪽에서 결정할 수 있다.</li>\n<li>예측 가능하고 이해하기 쉬우며 디버깅이 용이한 동시에 유지보수가 수월해질 것</li>\n</ul>\n<h3 id=\"-642-명령-쿼리-분리와-참조-투명성\" style=\"position:relative;\"><a href=\"#-642-%EB%AA%85%EB%A0%B9-%EC%BF%BC%EB%A6%AC-%EB%B6%84%EB%A6%AC%EC%99%80-%EC%B0%B8%EC%A1%B0-%ED%88%AC%EB%AA%85%EC%84%B1\" aria-label=\" 642 명령 쿼리 분리와 참조 투명성 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>🔖 6.4.2 명령-쿼리 분리와 참조 투명성</h3>\n<p>명령과 쿼리를 분리함으로써 명령형 언어의 틀 안에서 **참조 투명성(referential transparency)**의 장점을 제한적이나마 누릴 수 있게 된다.</p>\n<ul>\n<li>참조 투명성이란 어떤 표현식 e가 있을 때 e의 값으로 e가 나타나는 모든 위치를 교체하더라도 결과가 달라지지 않는 특성</li>\n<li>참조 투명성을 잘 활용하면 버그가 적고, 디버깅이 용이하며, 쿼리의 순서에 따라 실행 결과가 변하지 않는 코드를 작성할 수 있다.</li>\n<li>컴퓨터의 세계와 수학의 세계를 나누는 가장 큰 특징은 <strong>부수효과(side effect)</strong> 의 존재 유무다.</li>\n<li>어떤 값이 변하지 않는 성질을 <strong>불변성(immutability)</strong> 이라고 부른다.</li>\n</ul>\n<p>참조 투명성을 만족하는 식은 우리에게 두 가지 장점을 제공</p>\n<ol>\n<li>모든 함수를 이미 알고 있는 하나의 결괏값으로 대체할 수 있기 때문에 식을 쉽게 계산할 수 있다.</li>\n<li>모든 곳에서 함수의 결괏값이 동일하기 때문에 식의 순서를 변경하더라도 각 식의 결과는 달라지지 않는다.</li>\n</ol>\n<h3 id=\"-643-책임에-초점을-맞춰라\" style=\"position:relative;\"><a href=\"#-643-%EC%B1%85%EC%9E%84%EC%97%90-%EC%B4%88%EC%A0%90%EC%9D%84-%EB%A7%9E%EC%B6%B0%EB%9D%BC\" aria-label=\" 643 책임에 초점을 맞춰라 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>🔖 6.4.3 책임에 초점을 맞춰라</h3>\n<p>메시지를 먼저 선택하고 그 후에 메시지를 처리할 객체를 선택하라.</p>\n<ul>\n<li>디미터 법칙\n<ul>\n<li>협력이라는 컨텍스트 안에서 객체보다 메시지를 먼저 결정하면 두 객체 사이의 구조적인 결합도를 낮출 수 있다.</li>\n</ul>\n</li>\n<li>묻지 말고 시켜라\n<ul>\n<li>메시지를 먼저 선택하면 협력을 구조화하게 된다.</li>\n</ul>\n</li>\n<li>의도를 드러내는 인터페이스\n<ul>\n<li>메시지를 먼저 선택한다는 것은 메시지를 전송하는 클라이언트의 관점에서 메시지의 이름을 정한다는 것</li>\n</ul>\n</li>\n<li>명령-쿼리 분리 원칙\n<ul>\n<li>메시지를 먼저 선택한다는 것은 협력이라는 문맥 안에서 객체의 인터페이스에 관해 고민한다는 것을 의미</li>\n</ul>\n</li>\n</ul>\n<p>협력을 위해 두 객체가 보장해야 하는 실행 시점의 제약을 인터페이스에 명시할 수 있는 방법이 존재하지 않음.</p>\n<ul>\n<li>시그니처에는 어떤 조건이 만족돼야만 오퍼레이션을 호출할 수 있고 어떤 경우에 결과를 반환받을 수 없는지를 표현할 수 없다.</li>\n<li>이 문제를 해결하기 위해 <strong>계약에 의한 설계(Design By Contract)</strong> 개념이 나옴.</li>\n</ul>","excerpt":"애플리케이션은 클래스로 구성되지만 메시지를 통해 정의된다는 사실을 기억하라. 📖 6.1 협력과 메시지 🔖 6.1.1 클라이언트-서버 모델 메시지는 객체 사이의 협력을 가능하게 하는 매개체다. 두 객체 사이의 협력 관계를 설명하기 위해 사용하는 전통적인 메타포는 클라이언트-서버 모델 이다. Movie…","fields":{"slug":"/backend/object/chapter6/메시지와_인터페이스/"},"frontmatter":{"title":"Object - 6장 메시지와 인터페이스","thumbnail":{"childImageSharp":{"fluid":{"src":"/static/dd0e1990925e942697544e2bdcd9332e/9b73b/object.png"}}},"draft":false,"category":"Back-End","tags":["java"],"date":"October 01, 2023"}}}},"staticQueryHashes":["2374173507","2996537568","3691437124"]}