class Circle {
void drawWithRedColor() {
// 빨간색 원 그리기
}
void drawWithBlueColor() {
// 파란색 원 그리기
}
}
class Square {
void drawWithRedColor() {
// 빨간색 사각형 그리기
}
void drawWithBlueColor() {
// 파란색 사각형 그리기
}
}
이 코드에서 Circle과 Square 클래스는 각각 빨간색과 파란색으로 도형을 그리는 메서드를 가지고 있어요. 지금은 두 가지 도형과 색상만 다루고 있죠.
그런데 여기서 도형의 종류가 더 늘어나면 어떻게 될까요? 또는 색상이나 그리기 방식이 더 다양해지면 어떨까요? 🤔
'아, 그냥 도형 클래스에 색상 속성을 추가하고 메서드를 더 만들면 되지!'라고 생각할 수도 있어요. 실제로 이렇게 하는 게 간단하고, 유지보수도 그다지 어렵지 않을 수 있죠.
하지만, 만약 그려야 할 도형이 원, 삼각형, 정사각형, n각형 등으로 늘어나고, 각각의 도형마다 빨강, 노랑, 초록, 파랑 같은 색상을 추가해야 한다면? 조합이 늘어날수록 필요한 클래스도 기하급수적으로 늘어나는 걸 상상해 보세요. 😔
그러면 어떻게 될까요? 당연히 코드가 급격하게 늘어나고, 복잡해지며 유지보수가 어려워지겠죠... 😠
브릿지 패턴(Bridge Pattern)
브릿지 패턴은 도형과 색상을 분리해, 서로 독립적으로 확장할 수 있도록 도와줍니다. 예를 들어, 원과 사각형 같은 도형이 있고, 빨강과 파랑 같은 색상 클래스가 있다고 가정해 볼게요. 브릿지 패턴의 핵심은 바로 이 두 개의 요소를 조합해 주는 역할을 한다는 거예요.
이렇게 되면, "원을 빨강로 칠하고, 사각형을 파란색으로 칠하는" 식으로 자유롭게 조합할 수 있습니다. 새로운 색상을 추가하더라도 기존 도형 클래스는 수정할 필요가 없고, 새로운 색상 클래스를 추가하기만 하면 되니 확장이 훨씬 쉬워지는 거죠. 😎
interface Color {
void applyColor();
}
Color 인터페이스는 색상을 적용하는 메서드인 applyColor()를 정의합니다. 이 인터페이스는 브릿지 패턴에서 구현 계층의 역할을 맡고 있어요. 구체적인 색상 구현 클래스들이 이 인터페이스를 구현하게 될 거예요.
이 인터페이스를 사용하면 다양한 색상 클래스를 동일한 방식으로 처리할 수 있습니다. Shape 클래스는 특정 색상 클래스에 종속되지 않도록 되어 있으며, 새로운 색상을 추가할 때도 이 인터페이스만 구현하면 되기 때문에 유연성을 제공하죠. 😊
class RedColor implements Color {
@Override
public void applyColor() {
System.out.println("Applying red color");
}
}
class BlueColor implements Color {
@Override
public void applyColor() {
System.out.println("Applying blue color");
}
}
이제 Color 인터페이스를 구현한 구체적인 색상 클래스를 살펴볼까요? RedColor와 BlueColor 클래스는 각각 빨강과 파랑으로 도형에 색을 입힐 수 있습니다. 브릿지 패턴에서 구체적인 구현은 인터페이스를 통해 분리되며, 이 클래스들은 applyColor() 메서드를 오버라이드하여 자신만의 색상 적용 방식을 정의합니다.
새로운 색상을 추가할 때는, 이제 이 패턴을 확장하여 새로운 Color 구현체를 만들기만 하면 되죠! 확장성이 뛰어나죠?
abstract class Shape {
protected Color color;
protected Shape(Color color) {
this.color = color;
}
abstract void draw();
}
Shape 클래스는 브릿지 패턴에서 추상화 계층의 역할을 합니다. 도형과 색상 간의 관계를 분리해, 도형의 종류와 색상을 독립적으로 확장할 수 있도록 해주죠. 이 구조 덕분에, 계층 구조의 복잡성이 줄어들게 됩니다.
코드를 보면, Shape 클래스는 Color 타입의 참조 변수를 가지고 있고, 이를 통해 색상과 관련된 작업을 위임할 수 있어요. 그리고 draw() 메서드는 구체적인 도형 클래스에서 구현해야 할 추상 메서드로 정의되어 있습니다.
class Circle extends Shape {
protected Circle(Color color) {
super(color);
}
@Override
void draw() {
System.out.print("Circle drawn. ");
color.applyColor();
}
}
마지막으로, Shape 클래스를 상속한 Circle과 Square 클래스를 보면, 각각의 도형이 어떻게 그려져야 하는지를 정의합니다. 이 클래스들은 Shape 클래스에서 정의된 추상 메서드 draw()를 구체적으로 구현해, 도형의 종류에 따라 추가적인 기능을 제공할 수 있죠.
이렇게 브릿지 패턴을 적용하면, 도형과 색상을 독립적으로 관리하면서도 필요한 조합을 자유롭게 만들어낼 수 있어요. 도형이 늘어나거나 색상이 추가되더라도 기존 코드를 크게 변경하지 않아도 되기 때문에, 유지보수와 확장에 강력한 장점을 제공합니다. 💪
전략 패턴에 대해서
그렇지만, 단순히 "원에 빨간색, 사각형에는 파란색" 정도만 필요한 상황에서도 브릿지 패턴을 쓰는 게 과연 맞을까요?
괜히 더 복잡해질 것 같다는 생각이 들지 않나요? 정말 간단한 작업에 불필요하게 인터페이스나 추상 클래스를 만들고, 이것저것 연결하다 보면 코드가 오히려 복잡해지고 읽기 어려워질 수 있어요.
이럴 때는 차라리 전략 패턴을 사용하는 게 더 나을 수 있어요. 전략 패턴은 "도형을 그리는 전략"을 하나의 클래스로 캡슐화하고, 필요할 때 그 전략을 바꿔치기하는 방식이죠. 예를 들어, "빨간색 원 그리기"라는 전략과 "파란색 사각형 그리기"라는 전략을 만들어서, 그때그때 필요한 전략을 선택해서 사용하는 식이에요. 🤔
interface DrawingStrategy {
void draw();
}
class RedCircleDrawing implements DrawingStrategy {
@Override
public void draw() {
System.out.println("Drawing a red circle");
}
}
class Shape {
private DrawingStrategy strategy;
public Shape(DrawingStrategy strategy) {
this.strategy = strategy;
}
public void draw() {
strategy.draw();
}
}
이 방법은 구조를 단순하게 유지하면서도, 새로운 전략을 추가하기가 쉬워집니다. 클래스 계층이 깊어지지 않고, 각 전략이 독립적이기 때문에 코드가 훨씬 깔끔해질 수 있겠죠!
결론적으로, 브릿지 패턴을 사용할지 말지 결정할 때는 "이게 정말 필요한 복잡성인가?"를 꼭 생각해봐야 할 것 같아요. 🤔 조합이 많고, 계속 확장될 가능성이 높다면 브릿지 패턴이 좋은 선택이 될 수 있지만, 단순한 상황이라면 전략 패턴처럼 더 직관적이고 간단한 방법이 더 나을 수 있을 거예요. 😊
'Computer Science > Design Pattern' 카테고리의 다른 글
[Design Pattern] 옵저버 패턴(Observer Pattern)에 대해서 - 컴도리돌이 (0) | 2024.08.27 |
---|---|
[Design Pattern] 플라이웨이트 패턴(Flyweight Pattern)에 대해서 - 컴도리돌이 (0) | 2024.08.16 |
[Design Pattern] 싱글톤 패턴(Singleton Pattern)에 대해서 - 컴도리돌이 (0) | 2024.08.13 |
[Design Pattern] 빌더 패턴(Builder Pattern)에 대해서, @Builder - 컴도리돌이 (0) | 2024.05.05 |
[Design Pattern] 퍼사드 패턴(Facade Pattern)에 대해서 - 컴도리돌이 (0) | 2024.04.04 |