디자인 패턴이 너무 어려워서 정리하기 위한 포스팅 / 개인 기록용
Bridge Pattern
브릿지 패턴은 복잡하거나, 큰 클래스를 좀 더 추상화하여 분리하는 패턴이다. 좀 더 추상화 한다는 게 어떤 의미일까?
도형과 색깔이 있다고 하자. 경우의 수는 빨간색 원, 빨간색 직사각형, 파란색 원, 파란색 직사각형이 된다.
이후 오각형이 추가되었고 빨간색 오각형, 파란색 오각형이 필요하여 2종류가 추가 되었다.
이게 클래스라고 가정한다면 도형이 추가될 때 마다 자연스럽게 엄청 많은 클래스가 생길 것이다.
근데 잘 생각 해보면 빨간 사각형, 파란 사각형처럼 도형 자체에서 상속을 통해 색을 표현하기 보다는
도형이 색이라는 것을 소유하는 게 더 효율적일 수 있다.
즉 Shape이라는 클래스가 Color라는 별도의 계층을 가지도록 하는 것이다. 코드로 살펴보자
abstract class Shape {
abstract fun draw()
}
class RedTriangle : Shape() {
override fun draw() {
for (i in 1..3) {
println("draw Red Line")
}
}
}
class BlueTriangle : Shape() {
override fun draw() {
for (i in 1..3) {
println("draw Blue Line")
}
}
}
class RedRectangle : Shape() {
override fun draw() {
for (i in 1..4) {
println("draw Red Line")
}
}
}
class BlueRectangle : Shape() {
override fun draw() {
for (i in 1..4) {
println("draw Blue Line")
}
}
}
class RedPentagon : Shape() {
override fun draw() {
for (i in 1..5) {
println("draw Red Line")
}
}
}
class BluePentagon : Shape() {
override fun draw() {
for (i in 1..5) {
println("draw Blue Line")
}
}
}
단지 삼각형, 사각형, 오각형에 빨간색, 파란색만 가지고 있을 뿐인데 클래스는 3*2 (도형 수 * 색깔 수) = 6이 되어버렸다.
그런데 만약 색을 별도의 계층으로 분리한다면?
abstract class Shape(
private val color: Color,
) {
abstract fun draw()
}
interface Color {
fun color(): String
}
class Triangle(private val color: Color) : Shape(color) {
override fun draw() {
for (i in 1..3) {
println("draw ${color.color()} Line")
}
}
}
class Rectangle(private val color: Color) : Shape(color) {
override fun draw() {
for (i in 1..4) {
println("draw ${color.color()} Line")
}
}
}
class Pentagon(private val color: Color) : Shape(color) {
override fun draw() {
for (i in 1..5) {
println("draw ${color.color()} Line")
}
}
}
class Blue : Color {
override fun color(): String {
return "Blue"
}
}
class Red : Color {
override fun color(): String {
return "Red"
}
}
fun main() {
val redTriangle = Triangle(Red())
val blueTriangle = Triangle(Blue())
val redRectangle = Rectangle(Red())
val blueRectangle = Rectangle(Blue())
val redPentagon = Pentagon(Red())
val bluePentagon = Pentagon(Blue())
}
색을 별도의 계층으로 분리하고 N각형에서 색을 참조하면 위와 같은 구조로 바뀐다.
물론 지금도 3 + 2 = 5로 별 차이가 안나는 것 같지만 나중에 십각형까지 존재하며 색깔이 10종류로 바뀐다면
클래스의 개수는 20 vs 100개가 된다.
이렇듯 브릿지 패턴은 상속을 통한 구체화에서 발생할 수 있는 클래스 폭발 문제와 복잡해짐을 합성으로 해결하는 패턴이다.
Adapter Pattern
어뎁터 패턴은 클라이언트가 A 서비스를 이용하려고 할 때 호환되지 않는 경우 중간에서 Adapter가 클라이언트의 요청을 잘 가공해서
대신 A 서비스에게 요청을 전달하는 패턴이다.
class Json {}
class Xml {}
class JsonStorageService() {
fun save(data: Json) {
println("Saving Json data")
}
}
class JsonStorageServiceAdapterForXML {
private val jsonStorageService = JsonStorageService()
fun save(data: Xml) {
jsonStorageService.save(xmlToJson(data))
}
private fun xmlToJson(data: Xml): Json {
return Json()
}
}
fun main() {
val xml = Xml()
val jsonStorageServiceAdapterForXML = JsonStorageServiceAdapterForXML()
jsonStorageServiceAdapterForXML.save(xml)
}
일반적인 Adapter 패턴이다. Json, Xml 클래스가 있다.
클라이언트는 XML 데이터를 저장해야하지만 저장소는 JsonStorage 하나 뿐이다.
그렇다고 현재 JsonStorage를 수정할 수 없다. 왜냐하면 너무 레거시이고 외부 라이브러리이기 때문이다.
따라서 Adapter를 통해 처리한다. adapter가 XML 데이터를 JSON으로 변환해주고 jsonStorageService를 호출해서 저장해준다.
위는 엄청 간단한 예시이고 위 코드처럼 직접 구체 클래스를 참조하는 것이 아닌 Adapter 인터페이스를 참조하는 것이 좋을 수도 있다.
어뎁터 패턴을 어떻게 구현하는 지도 중요하지만 어뎁터 패턴의 핵심은 호환되지 않는 것들을 연결해주는 패턴이란 것이다.
'cs 공부 기록용' 카테고리의 다른 글
[디자인 패턴] Factory Method, Abstract Factory (1) | 2025.05.25 |
---|---|
[디자인 패턴] Proxy, Decorator (0) | 2025.05.24 |
[디자인 패턴] Strategy, Template Method (0) | 2025.05.23 |
운영체제 기록용 (0) | 2025.02.08 |