|
| 1 | +## 개요 |
| 2 | +클래스의 인터페이스를 사용자가 기대하는 인터페이스 형태로 적응(변환)시킵니다. 서로 일치하지 않는 인터페이스를 갖는 클래스들을 함께 동작시킵니다. |
1 | 3 |
|
| 4 | +## 다른이름 |
| 5 | +래퍼(Wrapper) |
| 6 | + |
| 7 | +## 동기 |
| 8 | +가끔은 재사용을 목표로 개발한 툴킷도 실제 재사용성을 발휘하지 못할 때가 발생한다. 즉, 응용프로그램이 요청하는 인터페이스와 툴킷에 정의된 인터페이스가 일치하지 않을 때가 있기 때문. |
| 9 | + |
| 10 | +편집기로 예를들면, 그림 편집기는 사용자가 선, 다각형, 텍스트 등 그래픽 요소를 이용하여 그림과 다이어그램을 만들 수 있는 프로그램이다. 그림판의 중요한 개념은 그래픽 객체들인데, 이들은 모양을 편집할 수 있는 특징을 가지며, 화면에 그려질 수 있는 행동을 정의한다. 이러한 공통점을 Shape라는 추상적인 인터페이스로 정의를 해봅시다. 그 후 우리는 편집기의 각각의 그래픽 요소를 Shape의 서브클래스로 정의해야 한다. 즉, 선을 위한 LineShape, 다각형을 위한 PolygonShape 등 클래스를 개발 한다. |
| 11 | + |
| 12 | +LineShape나 PolygonShape 클래스와 같이 아주 기본적인 그래픽 요소들은 제한적인 기능들로 이루어져 있어 구현하기 쉽다. 그러나 TextShape는 텍스트를 처리하기 위해 화면을 수정하고 버퍼를 관리하는 등 복잡한 기능을 구현해야 한다. 그래서 TextShape 클래스를 구현할때는 툴킷에서 제공되는 TextView를 재사용하는것이 바람직 하지만, 기존 구현된 툴킷은 현재 새롭게 개발된 Shape 클래스를 고려해서 개발된 것이 아니므로 바로 TextShape의 대체해 사용할 수 있는 상황이 안된다. |
| 13 | + |
| 14 | +그렇다면 서로 일치하지 않는 인터페이스를 갖는 클래스들을 잘 통합하여 하나의 응용프로그램으로 구성하여 운영해야 할때 어떤 방법이 있을까? 다시 말해 , 기존에 만든 TextView의 인터페이스를 변경해 Shape의 인터페이스와 일치하게 만들 수 있을까? 소스가 있다면 가능할 수 있겠지만 없다면 TextView를 변경하는 것은 불가능 하다. |
| 15 | + |
| 16 | +이미 개발된 클래스의 인터페이스를 수정할 수 없다면, Shape와 TextView 인터페이스에 맞도록 우리가 개발한 TextShape 클래스를 조정을 해야 한다. 이를 위해 우리가 할 수 있는 일은 다음과 같다. |
| 17 | +1. Shape의 인터페이스와 TextView의 구현을 모두 상속을 받는다 |
| 18 | +2. TextView의 인스턴스를 TextShape에 포함시키고, TextView 인터페이스를 사용하여 TextShape를 구현한다. |
| 19 | + |
| 20 | +이들 두 가지 방법은 적응자 패턴의 클래스 버전 및 객체 버전에 해당합니다 |
| 21 | + |
| 22 | +## 예제코드 |
| 23 | +```kotlin |
| 24 | +data class Rect( |
| 25 | + val x: Int, |
| 26 | + val y: Int |
| 27 | +) |
| 28 | + |
| 29 | +data class Bound( |
| 30 | + val left: Int, |
| 31 | + val top: Int, |
| 32 | + val right: Int, |
| 33 | + val bottom: Int |
| 34 | +) |
| 35 | + |
| 36 | +interface Shape { |
| 37 | + val width: Int |
| 38 | + val height: Int |
| 39 | + |
| 40 | + fun getBound(): Bound |
| 41 | +} |
| 42 | + |
| 43 | +interface TextShape : Shape { |
| 44 | + fun move(x: Int, y: Int) |
| 45 | + fun getText(): String |
| 46 | + fun editText(text: String) |
| 47 | +} |
| 48 | + |
| 49 | +interface TextView { |
| 50 | + var text: String |
| 51 | + var rect: Rect |
| 52 | + fun isEmpty(): Boolean |
| 53 | +} |
| 54 | + |
| 55 | +class MoveableTextShape( |
| 56 | + private val textView: TextView, |
| 57 | + override val width: Int = 0, |
| 58 | + override val height: Int = 0 |
| 59 | +) : TextShape { |
| 60 | + override fun move(x: Int, y: Int) { |
| 61 | + textView.rect = Rect(x, y) |
| 62 | + } |
| 63 | + |
| 64 | + override fun getText(): String = textView.text |
| 65 | + |
| 66 | + override fun editText(text: String) { |
| 67 | + textView.text = text |
| 68 | + } |
| 69 | + |
| 70 | + override fun getBound(): Bound = with(textView.rect) { |
| 71 | + Bound(x, y, x + width, y + height) |
| 72 | + } |
| 73 | + |
| 74 | + override fun toString() = getText() |
| 75 | +} |
| 76 | + |
| 77 | +fun main() { |
| 78 | + val textView = object : TextView { |
| 79 | + override var text: String = "Hello" |
| 80 | + override var rect: Rect = Rect(0, 0) |
| 81 | + |
| 82 | + override fun isEmpty(): Boolean = text.isEmpty() |
| 83 | + } |
| 84 | + |
| 85 | + val textShape = MoveableTextShape(textView, 100, 150) |
| 86 | + |
| 87 | + println(textShape.getBound().toString()) // Bound(left=0, top=0, right=100, bottom=150) |
| 88 | + |
| 89 | + textShape.move(100, 100) |
| 90 | + println(textShape.getBound().toString()) // Bound(left=100, top=100, right=200, bottom=250) |
| 91 | + |
| 92 | + println(textShape) // Hello |
| 93 | + |
| 94 | + textShape.editText("World") // World |
| 95 | + println(textShape) |
| 96 | +} |
| 97 | +``` |
0 commit comments