From c27a1da5a7fd9e2951f1b873b7f33ec13ae3d6dd Mon Sep 17 00:00:00 2001 From: CJW23 Date: Thu, 3 Mar 2022 23:09:14 +0900 Subject: [PATCH 1/4] =?UTF-8?q?=EB=B8=8C=EB=A6=BF=EC=A7=80=20=ED=8C=A8?= =?UTF-8?q?=ED=84=B4=20-=20=EC=98=88=EC=A0=9C=20=EC=BD=94=EB=93=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../summary/example-code.md" | 264 ++++++++++++++++++ 1 file changed, 264 insertions(+) create mode 100644 "\352\265\254\354\241\260/4\354\243\274\354\260\250-\353\270\214\353\246\277\354\247\200/summary/example-code.md" diff --git "a/\352\265\254\354\241\260/4\354\243\274\354\260\250-\353\270\214\353\246\277\354\247\200/summary/example-code.md" "b/\352\265\254\354\241\260/4\354\243\274\354\260\250-\353\270\214\353\246\277\354\247\200/summary/example-code.md" new file mode 100644 index 0000000..e83f7ff --- /dev/null +++ "b/\352\265\254\354\241\260/4\354\243\274\354\260\250-\353\270\214\353\246\277\354\247\200/summary/example-code.md" @@ -0,0 +1,264 @@ +# 브릿지 패턴 예제 + +예제 코드 요구사항 + +- 기존에 채용 공고 페이지에 카카오맵 API를 이용한 회사 위치가 표현되고 있음 +- 하지만 카카오맵 API는 매일 18:00~20:00에 멈춤 현상이 있음 +- 대안으로 네이버 지도 API를 이용한 회사 위치 표현이 필요함 + +아래 코드는 기존 카카오맵 API만 적용됐을 때 코드라 가정하겠습니다. + +```java +/** + * 카카오맵 API + */ +public class KakaoMapAPI { + public void drawMap(String address) { + System.out.printf("카카오 맵에 주소 %s를 표현한다.\n", address); + } +} + +/** + * 채용 공고 페이지 클래스 + */ +public class RecruitPage { + //회사 주소 + private String address; + //카카오맵 API + private final KakaoMapAPI kakaoMapAPI = new KakaoMapAPI(); + + public RecruitPage(String address) { + this.address = address; + } + + //카카오맵에 표현 + public void drawMap() { + kakaoMapAPI.drawMap(address); + } +} + +public class Main { + public static void main(String[] args) { + RecruitPage recruitPage = new RecruitPage("서울시 구로구 디지털로 34길 43"); + //지도에 표현 + recruitPage.drawMap(); + } +} + +결과 +카카오 맵에 주소 서울시 구로구 디지털로 34길 43를 표현한다. +``` + +여기에 네이버맵 API를 적용한다 가정하고 일반적인 방법으로 구현해보면 다음처럼 구현할 수 있습니다. + +```java +/** + * 카카오맵 API + */ +public class KakaoMapAPI { + //카카오맵에 표현 + public void drawMap(String address) { + System.out.printf("카카오 맵에 주소 %s를 표현한다.\n", address); + } +} + +/** + * 네이버 지도 API + */ +public class NaverMapAPI { + private String address; + + //주소 저장 + public void setAddress(String address) { + this.address = address; + } + + //네이버 지도에 표현 + public void draw() { + System.out.printf("네이버 지도에 주소 %s를 표현합니다.\n", this.address); + } +} + +/** + * 채용 공고 페이지 클래스 + */ +public class RecruitPage { + //회사 주소 + private String address; + //카카오맵 API + private final KakaoMapAPI kakaoMapAPI = new KakaoMapAPI(); + //네이버 지도 API + private final NaverMapAPI naverMapAPI = new NaverMapAPI(); + + public RecruitPage(String address) { + //주소 저장 + this.address = address; + } + + //인자에 따라 카카오맵, 네이버 지도를 분기처리 + public void drawCompanyMap(String api) { + if(api.equals("naver")) { + naverMapAPI.setAddress(address); + naverMapAPI.draw(); + } else if(api.equals("kakao")) { + kakaoMapAPI.drawMap(address); + } + } +} + +public class Main { + public static void main(String[] args) { + RecruitPage recruitPage = new RecruitPage("서울시 구로구 디지털로 34길 43"); + //카카오맵 정상 작동 + recruitPage.drawCompanyMap("kakao"); + //카카오맵 멈춤 -> 네이버 지도 호출 + recruitPage.drawCompanyMap("naver"); + } +} + +결과 +카카오 맵에 주소 서울시 구로구 디지털로 34길 43를 표현한다. +네이버 지도에 주소 서울시 구로구 디지털로 34길 43를 표현합니다. +``` + +위 코드를 보면 기존 `RecruitPage` 클래스에 `NaverMapAPI` 클래스 객체를 새로 만든 것을 확인할 수 있다. 그리고 이로 인해 기존 코드 `drawMap()`가 변경됐다. 그렇다면 이후에 또 다른 API를 추가할 때마다 기존 코드가 변경되는 상황이 발생한다. + +즉 `OCP(Open/Closed Principle)`에 위배되는 상황이다. + +이제 브릿지 패턴을 적용해보자. 아래는 예제 코드의 다이어그램이다. + +![Untitled](https://user-images.githubusercontent.com/32676275/156580823-32e4d2e9-fb7a-4e0f-88b3-17f5942fce0e.png) + +```java +/** + * 카카오맵 API + */ +public class KakaoMapAPI { + //맵을 그리는 API 기능 + public void drawMap(String address) { + System.out.printf("카카오 맵에 주소 %s를 표현한다.\n", address); + } +} + +/** + * 네이버 지도 API + */ +public class NaverMapAPI { + //주소 + private String address; + + //주소 저장 + public void setAddress(String address) { + this.address = address; + } + + //맵을 그리는 API 기능 + public void draw() { + System.out.printf("네이버 지도에 주소 %s를 표현합니다.\n", this.address); + } +} + +/** + * 채용 공고에 보여줄 맵을 구현할 인터페이스 + * ** Implementor ** + */ +public interface Map { + //맵을 그리는 기능을 하는 메소드 + public void drawMap(String address); +} + +/** + * 카카오 맵 실제 구현부 + * ** ConcreteImplementor ** + */ +public class KakaoMap implements Map{ + //카카오맵 API를 사용 + private final KakaoMapAPI kakaoMapAPI = new KakaoMapAPI(); + + //맵을 그리는 기능 + @Override + public void drawMap(String address) { + //카카오맵 사용 방법대로 drawMap() 호출 + kakaoMapAPI.drawMap(address); + } +} + +/** + * 네이버 지도 실제 구현체 + * ** ConcreteImplementor ** + */ +public class NaverMap implements Map{ + //네이버 지도 API를 사용 + private final NaverMapAPI naverMapAPI = new NaverMapAPI(); + + //맵을 그리는 기능 + @Override + public void drawMap(String address) { + //네이버 지도 사용 방법대로 setter() 다음 draw() 호출 + naverMapAPI.setAddress(address); + naverMapAPI.draw(); + } +} + +/** + * 지도 API를 사용할 페이지 추상 클래스 + * ** Abstraction ** + */ +public abstract class Page { + protected String address; + protected Map map; //Map(* Implementor *) 객체를 가지고 있음 + + public Page(String address, Map map) { + this.address = address; + this.map = map; + } + + //기업 위치를 맵에 나타낼 메소드 + public abstract void drawCompanyMap(); +} + +/** + * 채용 공고 페이지 클래스 + * ** RefinedAbstraction ** + */ +public class RecruitPage extends Page { + + //생성자 + public RecruitPage(String address, Map map) { + super(address, map); + } + + //기업 위치를 맵에 나타낼 메소드 + @Override + public void drawCompanyMap() { + //각 Map 구현체의 메소드를 수행 + this.map.drawMap(this.address); + } +} + +//메인 클래스 +public class Main { + public static void main(String[] args) { + String address = "서울시 구로구 디지털로 34길 43"; + KakaoMap kakaoMap = new KakaoMap(); + NaverMap naverMap = new NaverMap(); + + //사용할 맵 API를 외부에서 주입해서 사용 + Page recruitPage1 = new RecruitPage(address, kakaoMap); + recruitPage1.drawCompanyMap(); + + Page recruitPage2 = new RecruitPage(address, naverMap); + recruitPage2.drawCompanyMap(); + + /* + 추후 다른 맵API를 사용해도 + Map구현체(KakaoMap, NaverMap 등)를 만들어 주입해주면 된다 + 확장 과정에서 기존 코드가 수정되지 않기 때문에 + OCP(Open/Closed Principle)를 만족하게 된다 + */ + } +} +결과 +카카오 맵에 주소 서울시 구로구 디지털로 34길 43를 표현한다. +네이버 지도에 주소 서울시 구로구 디지털로 34길 43를 표현합니다. +``` \ No newline at end of file From 7e6b56e99be5198ae47ff30e38fc848a1eae5ba1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EC=B0=BD=EC=84=AD?= Date: Thu, 10 Mar 2022 23:43:13 +0900 Subject: [PATCH 2/4] =?UTF-8?q?Add)=20=EB=B8=8C=EB=A6=BF=EC=A7=80=20?= =?UTF-8?q?=ED=8C=A8=ED=84=B4=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...7\354\247\200 \355\214\250\355\204\264.md" | 325 ++++++++++++++++++ 1 file changed, 325 insertions(+) create mode 100644 "\352\265\254\354\241\260/4\354\243\274\354\260\250-\353\270\214\353\246\277\354\247\200/summary/\353\270\214\353\246\277\354\247\200 \355\214\250\355\204\264.md" diff --git "a/\352\265\254\354\241\260/4\354\243\274\354\260\250-\353\270\214\353\246\277\354\247\200/summary/\353\270\214\353\246\277\354\247\200 \355\214\250\355\204\264.md" "b/\352\265\254\354\241\260/4\354\243\274\354\260\250-\353\270\214\353\246\277\354\247\200/summary/\353\270\214\353\246\277\354\247\200 \355\214\250\355\204\264.md" new file mode 100644 index 0000000..bddfa0f --- /dev/null +++ "b/\352\265\254\354\241\260/4\354\243\274\354\260\250-\353\270\214\353\246\277\354\247\200/summary/\353\270\214\353\246\277\354\247\200 \355\214\250\355\204\264.md" @@ -0,0 +1,325 @@ +# 브릿지 패턴 + +> **`추상`적인 것과 `구체`적인 것을 분리하여 연결하는 패턴** + +![https://www.notion.so/Users/LeeChnagSup/Library/Application%20Support/marktext/images/2022-03-01-17-45-58-image.png](https://upload.wikimedia.org/wikipedia/commons/thumb/c/cf/Bridge_UML_class_diagram.svg/750px-Bridge_UML_class_diagram.svg.png) + +밀접하게 관련된 클래스 집합을 서로 독립적으로 그리고 두 개별 계층(추상적인 파트와 구체적인 파트)으로 나눠서 서로 분할 할 수 있는 구조로 만들 수 있는 패턴 + +위의 도식에서 각 명칭에 대한 용어를 정리하자면 다음과 같다. + +`Abstraction` : 기능 계층의 최상위 클래스이며 추상 인터페이스 + +`RefinedAbstraction` : 기능 계층에서 새로운 부분을 확장할 클래스 + +`Implementor` : `Abstraction`의 기능을 구현하기 위한 인터페이스 정의 + +`ConcreteImplementor` : 실제 기능 구현 클래스 + + + +## 브릿지 패턴은 언제 사용되는가? + +예를 들어서 3D 모양을 구현하고 있는 객체가 있다고 가정해보자. 색과 그 3D모양이 지금은 단 각각 두가지(구와 사면체, 빨간색과 파란색)만 존재할때는 아래처럼 형태를 구성하면된다. + +![](https://refactoring.guru/images/patterns/diagrams/bridge/problem-en.png) + +근데 이렇게 되면, 빨간구체, 빨간 사면체, 파란 구체, 파랑 사면체와 같은 구체적인 클래스가 구성된다. + +**문제는 우리는 요구사항이 추가 될수 있다는 점이다.** + +*여기에 만약에.. 삼각뿔을 추가한다면? 초록색을 추가한다면?* 이렇게 되면 같은 코드를 여러번 반복해서 사용해야하고, 복잡도가 어마어마하게 증가할 것이다. + +이럴거면 추상적으로 `모양` 따로 `색깔` 따로 관리한다면 위의 요구사항에서 삼각뿔을 추가하가나, 초록색을 추가하는 행위가 어려움을 가지진 않을것이다. + +![](https://refactoring.guru/images/patterns/diagrams/bridge/solution-en.png) + +단지 우리는 `Color`에 `Green`을 추가하면 되는 것이고, `Shape`에는 `Triangle`을 추가하면 되는 거니까 이렇게 추상적인 상태로 분리하고 구체적인 클래스를 추가하는 식의 분할해서 연결하는 패턴으로 사용하게 된다면 위의 하나의 Shape에 구체적인 클래스를 왕창 몰아 넣을때보다 훨씬 더 확장하기 쉬워진다. + +## 브릿지 패턴 예제 코드 + +예제 코드 요구사항 + +- 기존에 채용 공고 페이지에 카카오맵 API를 이용한 회사 위치가 표현되고 있음 +- 하지만 카카오맵 API는 매일 18:00~20:00에 멈춤 현상이 있음 +- 대안으로 네이버 지도 API를 이용한 회사 위치 표현이 필요함 + +아래 코드는 기존 카카오맵 API만 적용됐을 때 코드라 가정하겠습니다. + +```java +/** + * 카카오맵 API + */ +public class KakaoMapAPI { + public void drawMap(String address) { + System.out.printf("카카오 맵에 주소 %s를 표현한다.\n", address); + } +} + +/** + * 채용 공고 페이지 클래스 + */ +public class RecruitPage { + //회사 주소 + private String address; + //카카오맵 API + private final KakaoMapAPI kakaoMapAPI = new KakaoMapAPI(); + + public RecruitPage(String address) { + this.address = address; + } + + //카카오맵에 표현 + public void drawMap() { + kakaoMapAPI.drawMap(address); + } +} + +public class Main { + public static void main(String[] args) { + RecruitPage recruitPage = new RecruitPage("서울시 구로구 디지털로 34길 43"); + //지도에 표현 + recruitPage.drawMap(); + } +} + +결과 +카카오 맵에 주소 서울시 구로구 디지털로 34길 43를 표현한다. +``` + +여기에 네이버맵 API를 적용한다 가정하고 일반적인 방법으로 구현해보면 다음처럼 구현할 수 있습니다. + +```java +/** + * 카카오맵 API + */ +public class KakaoMapAPI { + //카카오맵에 표현 + public void drawMap(String address) { + System.out.printf("카카오 맵에 주소 %s를 표현한다.\n", address); + } +} + +/** + * 네이버 지도 API + */ +public class NaverMapAPI { + private String address; + + //주소 저장 + public void setAddress(String address) { + this.address = address; + } + + //네이버 지도에 표현 + public void draw() { + System.out.printf("네이버 지도에 주소 %s를 표현합니다.\n", this.address); + } +} + +/** + * 채용 공고 페이지 클래스 + */ +public class RecruitPage { + //회사 주소 + private String address; + //카카오맵 API + private final KakaoMapAPI kakaoMapAPI = new KakaoMapAPI(); + //네이버 지도 API + private final NaverMapAPI naverMapAPI = new NaverMapAPI(); + + public RecruitPage(String address) { + //주소 저장 + this.address = address; + } + + //인자에 따라 카카오맵, 네이버 지도를 분기처리 + public void drawCompanyMap(String api) { + if(api.equals("naver")) { + naverMapAPI.setAddress(address); + naverMapAPI.draw(); + } else if(api.equals("kakao")) { + kakaoMapAPI.drawMap(address); + } + } +} + +public class Main { + public static void main(String[] args) { + RecruitPage recruitPage = new RecruitPage("서울시 구로구 디지털로 34길 43"); + //카카오맵 정상 작동 + recruitPage.drawCompanyMap("kakao"); + //카카오맵 멈춤 -> 네이버 지도 호출 + recruitPage.drawCompanyMap("naver"); + } +} + +결과 +카카오 맵에 주소 서울시 구로구 디지털로 34길 43를 표현한다. +네이버 지도에 주소 서울시 구로구 디지털로 34길 43를 표현합니다. +``` + +위 코드를 보면 기존 `RecruitPage` 클래스에 `NaverMapAPI` 클래스 객체를 새로 만든 것을 확인할 수 있다. 그리고 이로 인해 기존 코드 `drawMap()`가 변경됐다. 그렇다면 이후에 또 다른 API를 추가할 때마다 기존 코드가 변경되는 상황이 발생한다. + +즉 `OCP(Open/Closed Principle)`에 위배되는 상황이다. + +이제 브릿지 패턴을 적용해보자. 아래는 예제 코드의 다이어그램이다. + +![Untitled](https://user-images.githubusercontent.com/32676275/156580823-32e4d2e9-fb7a-4e0f-88b3-17f5942fce0e.png) + +```java +/** + * 카카오맵 API + */ +public class KakaoMapAPI { + //맵을 그리는 API 기능 + public void drawMap(String address) { + System.out.printf("카카오 맵에 주소 %s를 표현한다.\n", address); + } +} + +/** + * 네이버 지도 API + */ +public class NaverMapAPI { + //주소 + private String address; + + //주소 저장 + public void setAddress(String address) { + this.address = address; + } + + //맵을 그리는 API 기능 + public void draw() { + System.out.printf("네이버 지도에 주소 %s를 표현합니다.\n", this.address); + } +} + +/** + * 채용 공고에 보여줄 맵을 구현할 인터페이스 + * ** Implementor ** + */ +public interface Map { + //맵을 그리는 기능을 하는 메소드 + public void drawMap(String address); +} + +/** + * 카카오 맵 실제 구현부 + * ** ConcreteImplementor ** + */ +public class KakaoMap implements Map{ + //카카오맵 API를 사용 + private final KakaoMapAPI kakaoMapAPI = new KakaoMapAPI(); + + //맵을 그리는 기능 + @Override + public void drawMap(String address) { + //카카오맵 사용 방법대로 drawMap() 호출 + kakaoMapAPI.drawMap(address); + } +} + +/** + * 네이버 지도 실제 구현체 + * ** ConcreteImplementor ** + */ +public class NaverMap implements Map{ + //네이버 지도 API를 사용 + private final NaverMapAPI naverMapAPI = new NaverMapAPI(); + + //맵을 그리는 기능 + @Override + public void drawMap(String address) { + //네이버 지도 사용 방법대로 setter() 다음 draw() 호출 + naverMapAPI.setAddress(address); + naverMapAPI.draw(); + } +} + +/** + * 지도 API를 사용할 페이지 추상 클래스 + * ** Abstraction ** + */ +public abstract class Page { + protected String address; + protected Map map; //Map(* Implementor *) 객체를 가지고 있음 + + public Page(String address, Map map) { + this.address = address; + this.map = map; + } + + //기업 위치를 맵에 나타낼 메소드 + public abstract void drawCompanyMap(); +} + +/** + * 채용 공고 페이지 클래스 + * ** RefinedAbstraction ** + */ +public class RecruitPage extends Page { + + //생성자 + public RecruitPage(String address, Map map) { + super(address, map); + } + + //기업 위치를 맵에 나타낼 메소드 + @Override + public void drawCompanyMap() { + //각 Map 구현체의 메소드를 수행 + this.map.drawMap(this.address); + } +} + +//메인 클래스 +public class Main { + public static void main(String[] args) { + String address = "서울시 구로구 디지털로 34길 43"; + KakaoMap kakaoMap = new KakaoMap(); + NaverMap naverMap = new NaverMap(); + + //사용할 맵 API를 외부에서 주입해서 사용 + Page recruitPage1 = new RecruitPage(address, kakaoMap); + recruitPage1.drawCompanyMap(); + + Page recruitPage2 = new RecruitPage(address, naverMap); + recruitPage2.drawCompanyMap(); + + /* + 추후 다른 맵API를 사용해도 + Map구현체(KakaoMap, NaverMap 등)를 만들어 주입해주면 된다 + 확장 과정에서 기존 코드가 수정되지 않기 때문에 + OCP(Open/Closed Principle)를 만족하게 된다 + */ + } +} +결과 +카카오 맵에 주소 서울시 구로구 디지털로 34길 43를 표현한다. +네이버 지도에 주소 서울시 구로구 디지털로 34길 43를 표현합니다. +``` + +## 패턴의 장/단점 + +장점: + +- 플렛폼 독립적인 클래스나, 앱을 만들 수 있다. +- 클라이언트 코드가 상당히 높은 수준의 추상화로 작동한다. +- OCP(개방-폐쇄 원칙)을 지킨다. 새로운 추상화와 구현을 서로 독립적으로 도입할 수 있다. +- SRP(단일 책임 원칙)을 지킨다. 추상화의 고급 논리와 플렛폼의 세부적인 정보에 초점을 맞출 수 있다. + +단점: + +- 패턴을 매우 응집력 있는 클래스에 적용하게 되면, 코드가 엄청 복잡해질 수 있다. + +## 비슷한 패턴 + +- **브릿지** 패턴은 응용 프로그램의 일부를 서로 독립적으로 개발할 수 있게 끔 설계하지만, 반면 **어뎁터** 패턴은 호환되지 않는 일부 클래스와 함께 잘 작동하도록 기존 앱과 함께 사용된다. + +- **브릿지**, **상태**, **전략**, **어뎁터 패턴의** 어떤 측면에서는 구조적으로 비슷한 형태를 가지고 있다. **실제로 이 모든 패턴은 다른 대상에 작업을 위임하는 형태로 되어있다.** 각각의 패턴은 모두 다른 문제를 해결하는 방식은 다릅니다. 이러한 패턴들은 결국 특정한 방식으로 구조화해서 해결하는 방법은 아니다. + +- 브릿지패턴은 **추상 팩토리** 패턴을 사용할 수 있다. 이 두 패턴을 동시에 사용하기 위해서는 브릿지에 의해 구성된 추상화 특별한 구현에서만 작동할 수 있을 때 유용하다. 이 경우에는 **추상 팩토리**는 이려한 연결들을 캡슐화 할 수 있고, 클라이언트 코드로부터 복잡도를 숨기는 효과를 가지고 있다. + +- 브릿지와 함께 **빌더** 패턴도 혼합할 수 있다. Director 클래스가 추상화의 역할을 맡고 다른 빌더들이 구현부를 맡아주면된다. From d053bb16cb42f52ec3dc5ad7b8f6729d82f0c5fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EC=B0=BD=EC=84=AD?= Date: Wed, 16 Mar 2022 23:12:46 +0900 Subject: [PATCH 3/4] =?UTF-8?q?fix)=20=EB=82=B4=EC=9A=A9=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...270\214\353\246\277\354\247\200 \355\214\250\355\204\264.md" | 2 -- 1 file changed, 2 deletions(-) diff --git "a/\352\265\254\354\241\260/4\354\243\274\354\260\250-\353\270\214\353\246\277\354\247\200/summary/\353\270\214\353\246\277\354\247\200 \355\214\250\355\204\264.md" "b/\352\265\254\354\241\260/4\354\243\274\354\260\250-\353\270\214\353\246\277\354\247\200/summary/\353\270\214\353\246\277\354\247\200 \355\214\250\355\204\264.md" index bddfa0f..499d2c4 100644 --- "a/\352\265\254\354\241\260/4\354\243\274\354\260\250-\353\270\214\353\246\277\354\247\200/summary/\353\270\214\353\246\277\354\247\200 \355\214\250\355\204\264.md" +++ "b/\352\265\254\354\241\260/4\354\243\274\354\260\250-\353\270\214\353\246\277\354\247\200/summary/\353\270\214\353\246\277\354\247\200 \355\214\250\355\204\264.md" @@ -16,8 +16,6 @@ `ConcreteImplementor` : 실제 기능 구현 클래스 - - ## 브릿지 패턴은 언제 사용되는가? 예를 들어서 3D 모양을 구현하고 있는 객체가 있다고 가정해보자. 색과 그 3D모양이 지금은 단 각각 두가지(구와 사면체, 빨간색과 파란색)만 존재할때는 아래처럼 형태를 구성하면된다. From 80e446e403cff88dfb3f984633cdd2cc528e5406 Mon Sep 17 00:00:00 2001 From: Choi Jae Woo <32676275+CJW23@users.noreply.github.com> Date: Tue, 22 Mar 2022 22:51:45 +0900 Subject: [PATCH 4/4] =?UTF-8?q?Update=20=EB=B8=8C=EB=A6=BF=EC=A7=80=20?= =?UTF-8?q?=ED=8C=A8=ED=84=B4.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 글 수정 --- ...7\354\247\200 \355\214\250\355\204\264.md" | 66 ++++++++++--------- 1 file changed, 35 insertions(+), 31 deletions(-) diff --git "a/\352\265\254\354\241\260/4\354\243\274\354\260\250-\353\270\214\353\246\277\354\247\200/summary/\353\270\214\353\246\277\354\247\200 \355\214\250\355\204\264.md" "b/\352\265\254\354\241\260/4\354\243\274\354\260\250-\353\270\214\353\246\277\354\247\200/summary/\353\270\214\353\246\277\354\247\200 \355\214\250\355\204\264.md" index 499d2c4..1d39ac6 100644 --- "a/\352\265\254\354\241\260/4\354\243\274\354\260\250-\353\270\214\353\246\277\354\247\200/summary/\353\270\214\353\246\277\354\247\200 \355\214\250\355\204\264.md" +++ "b/\352\265\254\354\241\260/4\354\243\274\354\260\250-\353\270\214\353\246\277\354\247\200/summary/\353\270\214\353\246\277\354\247\200 \355\214\250\355\204\264.md" @@ -6,7 +6,7 @@ 밀접하게 관련된 클래스 집합을 서로 독립적으로 그리고 두 개별 계층(추상적인 파트와 구체적인 파트)으로 나눠서 서로 분할 할 수 있는 구조로 만들 수 있는 패턴 -위의 도식에서 각 명칭에 대한 용어를 정리하자면 다음과 같다. +위의 도식에서 각 명칭 용어를 정리하자면 다음과 같다. `Abstraction` : 기능 계층의 최상위 클래스이며 추상 인터페이스 @@ -18,31 +18,33 @@ ## 브릿지 패턴은 언제 사용되는가? -예를 들어서 3D 모양을 구현하고 있는 객체가 있다고 가정해보자. 색과 그 3D모양이 지금은 단 각각 두가지(구와 사면체, 빨간색과 파란색)만 존재할때는 아래처럼 형태를 구성하면된다. +3D 모양을 구현한 객체가 있다고 하자. 색과 3D모양이 두가지씩(구와 사면체, 빨간색과 파란색) 존재할때는 아래처럼 형태를 구성할 수 있다. ![](https://refactoring.guru/images/patterns/diagrams/bridge/problem-en.png) -근데 이렇게 되면, 빨간구체, 빨간 사면체, 파란 구체, 파랑 사면체와 같은 구체적인 클래스가 구성된다. +위처럼 구성시 빨간구체, 빨간 사면체, 파란 구체, 파랑 사면체 4개의 클래스가 구성된다. **문제는 우리는 요구사항이 추가 될수 있다는 점이다.** -*여기에 만약에.. 삼각뿔을 추가한다면? 초록색을 추가한다면?* 이렇게 되면 같은 코드를 여러번 반복해서 사용해야하고, 복잡도가 어마어마하게 증가할 것이다. +*여기에 만약에.. 삼각뿔을 추가한다면? 초록색을 추가한다면?* 코드가 반복돼 복잡도가 증가한다. -이럴거면 추상적으로 `모양` 따로 `색깔` 따로 관리한다면 위의 요구사항에서 삼각뿔을 추가하가나, 초록색을 추가하는 행위가 어려움을 가지진 않을것이다. +이것을 추상적으로 `모양` 과 `색깔` 로 관리하면 삼각뿔과 초록색 추가가 용이해진다. ![](https://refactoring.guru/images/patterns/diagrams/bridge/solution-en.png) -단지 우리는 `Color`에 `Green`을 추가하면 되는 것이고, `Shape`에는 `Triangle`을 추가하면 되는 거니까 이렇게 추상적인 상태로 분리하고 구체적인 클래스를 추가하는 식의 분할해서 연결하는 패턴으로 사용하게 된다면 위의 하나의 Shape에 구체적인 클래스를 왕창 몰아 넣을때보다 훨씬 더 확장하기 쉬워진다. +위 구조에서 `Color`에 `Green`을 `Shape`에는 `Triangle`을 추가한 상태에서 `Green`과 `Triangle`을 연결해서 사용하면 된다. + +예제 코드를 살펴보자. ## 브릿지 패턴 예제 코드 예제 코드 요구사항 -- 기존에 채용 공고 페이지에 카카오맵 API를 이용한 회사 위치가 표현되고 있음 -- 하지만 카카오맵 API는 매일 18:00~20:00에 멈춤 현상이 있음 -- 대안으로 네이버 지도 API를 이용한 회사 위치 표현이 필요함 +- 기존 채용 공고 페이지에 카카오맵 API를 이용한 회사 위치 표시 +- 매일 18:00~20:00 카카오맵 API 멈춤 현상 발생 +- 대안으로 네이버 지도 API 도입 -아래 코드는 기존 카카오맵 API만 적용됐을 때 코드라 가정하겠습니다. +아래 코드는 카카오맵API만 적용됐을 때라 가정하자. ```java /** @@ -77,7 +79,7 @@ public class Main { public static void main(String[] args) { RecruitPage recruitPage = new RecruitPage("서울시 구로구 디지털로 34길 43"); //지도에 표현 - recruitPage.drawMap(); + recruitPage.drawMap(); } } @@ -85,14 +87,14 @@ public class Main { 카카오 맵에 주소 서울시 구로구 디지털로 34길 43를 표현한다. ``` -여기에 네이버맵 API를 적용한다 가정하고 일반적인 방법으로 구현해보면 다음처럼 구현할 수 있습니다. +이제 네이버 지도 API를 적용하면 다음처럼 구현할 수 있다. ```java /** * 카카오맵 API */ public class KakaoMapAPI { - //카카오맵에 표현 + //카카오맵에 표현 public void drawMap(String address) { System.out.printf("카카오 맵에 주소 %s를 표현한다.\n", address); } @@ -126,9 +128,9 @@ public class RecruitPage { //네이버 지도 API private final NaverMapAPI naverMapAPI = new NaverMapAPI(); - public RecruitPage(String address) { + public RecruitPage(String address) { //주소 저장 - this.address = address; + this.address = address; } //인자에 따라 카카오맵, 네이버 지도를 분기처리 @@ -157,11 +159,13 @@ public class Main { 네이버 지도에 주소 서울시 구로구 디지털로 34길 43를 표현합니다. ``` -위 코드를 보면 기존 `RecruitPage` 클래스에 `NaverMapAPI` 클래스 객체를 새로 만든 것을 확인할 수 있다. 그리고 이로 인해 기존 코드 `drawMap()`가 변경됐다. 그렇다면 이후에 또 다른 API를 추가할 때마다 기존 코드가 변경되는 상황이 발생한다. +위 예제에서 `RecruitPage` 클래스에 `NaverMapAPI` 클래스 객체가 새롭게 선언됐고 기존 `drawMap()`가 변경됐다. + +만약 이후에 API 추가되면 기존 코드가 또 변경될 것이다. 즉 `OCP(Open/Closed Principle)`에 위배되는 상황이다. -이제 브릿지 패턴을 적용해보자. 아래는 예제 코드의 다이어그램이다. +이제 브릿지 패턴을 적용해보자. 아래는 예제의 클래스 다이어그램이다. ![Untitled](https://user-images.githubusercontent.com/32676275/156580823-32e4d2e9-fb7a-4e0f-88b3-17f5942fce0e.png) @@ -170,7 +174,7 @@ public class Main { * 카카오맵 API */ public class KakaoMapAPI { - //맵을 그리는 API 기능 + //맵을 그리는 API 기능 public void drawMap(String address) { System.out.printf("카카오 맵에 주소 %s를 표현한다.\n", address); } @@ -180,7 +184,7 @@ public class KakaoMapAPI { * 네이버 지도 API */ public class NaverMapAPI { - //주소 + //주소 private String address; //주소 저장 @@ -199,7 +203,7 @@ public class NaverMapAPI { * ** Implementor ** */ public interface Map { - //맵을 그리는 기능을 하는 메소드 + //맵을 그리는 기능을 하는 메소드 public void drawMap(String address); } @@ -208,10 +212,10 @@ public interface Map { * ** ConcreteImplementor ** */ public class KakaoMap implements Map{ - //카카오맵 API를 사용 + //카카오맵 API를 사용 private final KakaoMapAPI kakaoMapAPI = new KakaoMapAPI(); - //맵을 그리는 기능 + //맵을 그리는 기능 @Override public void drawMap(String address) { //카카오맵 사용 방법대로 drawMap() 호출 @@ -224,10 +228,10 @@ public class KakaoMap implements Map{ * ** ConcreteImplementor ** */ public class NaverMap implements Map{ - //네이버 지도 API를 사용 + //네이버 지도 API를 사용 private final NaverMapAPI naverMapAPI = new NaverMapAPI(); - //맵을 그리는 기능 + //맵을 그리는 기능 @Override public void drawMap(String address) { //네이버 지도 사용 방법대로 setter() 다음 draw() 호출 @@ -264,7 +268,7 @@ public class RecruitPage extends Page { super(address, map); } - //기업 위치를 맵에 나타낼 메소드 + //기업 위치를 맵에 나타낼 메소드 @Override public void drawCompanyMap() { //각 Map 구현체의 메소드를 수행 @@ -286,12 +290,12 @@ public class Main { Page recruitPage2 = new RecruitPage(address, naverMap); recruitPage2.drawCompanyMap(); - /* - 추후 다른 맵API를 사용해도 - Map구현체(KakaoMap, NaverMap 등)를 만들어 주입해주면 된다 - 확장 과정에서 기존 코드가 수정되지 않기 때문에 - OCP(Open/Closed Principle)를 만족하게 된다 - */ + /* + 추후 다른 맵API를 사용해도 + Map구현체(KakaoMap, NaverMap 등)를 만들어 주입해주면 된다 + 확장 과정에서 기존 코드가 수정되지 않기 때문에 + OCP(Open/Closed Principle)를 만족하게 된다 + */ } } 결과