본문 바로가기

CS ﹒ Algorithm/DesignPatterns

디자인 패턴 (7) Bridge Pattern : 브릿지 패턴

 

브릿지 패턴이란?

 

하나의 구체 클래스를 추상 클래스와 구체 클래스로 분리한 뒤 연결하는 패턴이다.

아래의 간단한 예시를 보면서 가볍게 배워보고 JDBC에서 해당 패턴을 찾아보자.

 

이번에는 마땅히 아이디어가 떠오르지 않아 Baeldung의 소스코드를 참고했다.

 

https://www.baeldung.com/java-bridge-pattern

 

The Bridge Pattern in Java | Baeldung

A guide to the bridge design pattern and its Java implementation

www.baeldung.com

 

 

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
 
public interface Shape {
    void fill();
}
 
public class BlackTriangle implements Shape {
    @Override
    public void fill() {
        System.out.println("검은 삼각형 ~");
    }
}
 
public class RedSquare implements Shape {
    @Override
    public void fill() {
        System.out.println("정열의 네모 ~");
    }
}
 
public class YellowTriangle implements Shape {
    @Override
    public void fill() {
        System.out.println("개나리 삼각형~");
    }
}
 
cs

 

 

Shape 인터페이스를 상속받는 다음과 같은 클래스들이 있다.

실제로는 굉장히 복잡한 로직을 가지고 있으며 각 메서드마다 세부사항이 다르게 구현되어 있다고 생각해보자.

 

이 객체들은 "Color"와 "Shape"의 역할을 둘 다 가지고 있다.

둘 중 하나의 역할만 변경되거나 추가되더라도 매번 기존 구현 자체를 변경해야 한다.

 

 

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
@RequiredArgsConstructor
public class BaseShape implements Shape {
    private final Color color;
    private final String name;
 
    @Override
    public void fill() {
        System.out.println(this.color+" "+this.name);
   }
}
 
public interface Color {
    String getColor();
}
 
public class Blue implements Color {
    @Override
    public String getColor() {
        return "파랑파랑파랑";
    }
}
 
public class Purple implements Color {
    @Override
    public String getColor() {
        return "purple";
    }
}
 
public class Red implements Color{
    @Override
    public String getColor() {
        return "Red";
    }
}
cs

 

Shape을 상속하는 BaseShape 클래스와 Color 인터페이스를 생성해서 둘을 분리했다.

참고로 BaseShape도 인터페이스로 만들고 밑으로 다른 다양한 Shape을 상속해서 연결하는 상태도 마찬가지로 브릿지 패턴이다.

다만 제일 위에 썸네일로 올려놓은 JDBC와 같은 구조를 갖게하기 위해서 이런 형태로 구현했다.

 

 

 

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Shape_App {
    public static void main(String[] args) {
        // before
        Shape blackTriangle = new BlackTriangle();
        blackTriangle.fill();
        Shape redSquare = new RedSquare();
        redSquare.fill();
        Shape yellowTriangle = new YellowTriangle();
        yellowTriangle.fill();
        // after
        Shape blackTriangle2 = new BaseShape(new Blue(), "세모");
        blackTriangle2.fill();
        Shape redSquare2 = new BaseShape(new Red(), "네모네모");
        redSquare2.fill();
    }
}
cs

어쩌면 바꾸기 전보다 바꾼 뒤가 더 사용하기 불편해보일 수도 있다.

리얼 월드 코딩에서는 Shape blackTraingl2뒤에 따라오는 실제 구현은 DI 등으로 해결할 수 있음을 생각하자.

그러면 어느쪽이 훨씬 확장성이 좋은지 와닿을 것이다.

 

그리고 내가 JDBC의 다이어그램과 비슷한 형태를 보여주기 위해 이렇게 작성했다고 했는데, 무슨 말인지 확인해보자.

 

 

 

* 중요

JDBC 동작 방식은 브릿지 패턴과 연관지어 설명하기 위해 굉장히 축약했으며, 브릿지 패턴에 정확히 일치하는 디자인도 아닙니다.

어 ? 이건 이 패턴 아니야?라는 생각이 든다면 의도에 따라 해당 패턴으로 볼 수도 수도 있습니다.

 

이 Driver라는 인터페이스는 모든 형태의 DB Driver 클래스가 반드시 구현해야 한다.

String 타입의 url과 java.util.Properties타입의 info를 인자로 받아서 connect해서 Connection 객체를 반환하는데, 이것은 database를 연결할 url과 데이터베이스 접근에 필요한 user,password를 의미한다.

 

 

 

 

다들 mySql 등 DB를 연동해봤다면 이 설정은 익숙할 것이다.

바로 이 설정을 가져가서 읽어들인다.

 

 

 

 

 

이 메서드는 DriverManager 클래스 내부에 존재하는 메서드로, 어플리케이션 실행 시 jdbc.driver 경로에 위치한 모든 드라이버에게 각 url을 대입해서 적절한 드라이버 구현체를 찾고, 이후 여러 로직을 거쳐 Connection을 반환하게 한다.

 

즉, Driver는 오직 Driver의 역할만 가지며 여기서 반환 받은 Connection을 세션에서는 그냥 사용하기만 하기 때문에 Driver의 구현체가 아무리 추가되어도 전체 로직에는 아무런 영향도 주지 않는 것이다.

 

 

 

 

그러면 이런 브릿지 패턴을 이용해서 어떤 이점을 얻을 수 있을까?

당연히 추상적 코드를 이용해 구현체에 아무련 변경을 주지 않고 독립적으로 확장이 가능하다는 부분이다.(OCP)

또한 추상적 코드와 구체적 코드를 분리할 수 있기 때문에 유지보수도 더욱 편해진다.

 

다만 늘 객체를 세분화할 수록 계층 구조의 복잡도가 증가한다는 단점은 피할 수 없다.

하지만 난 300줄짜리 클래스 파일 100개 관리하기, 100줄짜리 클래스 파일 100줄짜리 클래스 파일 300개 관리하기 중에 하나만 고르라고 한다면 후자를 고를 것 같다. (물론 이 경우 작명이 굉장히 중요할 것이다.)

 

Adapter Pattern과의 차이점

1. Adapter Pattern : 구조가 모두 만들어진 뒤에 호환을 위해 별도의 인터페이스를 만들어서 호환시킴.

2. Bridge Pattern : 구조를 만드는 과정에서 역할을 분리해서 확장성을 높이는 것이 목표.