Notice
Recent Posts
Recent Comments
«   2024/05   »
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
Archives
Today
Total
관리 메뉴

DeFacto-Standard IT

Bridge Pattern (기본) 본문

Design Pattern/Structural Pattern

Bridge Pattern (기본)

defacto standard 2018. 2. 16. 23:52

1. 가정


 - 아톰을 만든다.

 - 아톰은 펀치공격이 가능하다.


2. Naive Code


- Punch

public class Punch {
private final String attackType = "Punch";

public String getName() {
return attackType;
}
}

펀치 공격에 해당하는 클래스.


 - Atom

public class Atom {
private final String robotType = "Atom";
private Punch punch;

public void setPunch(Punch punch) {
this.punch = punch;
}

public void attack() {
System.out.println(robotType + " : " + punch.getName());
}
}

아톰에 해당하는 클래스이다. 


 - NaiveClient

public class NaiveClient {
public static void main(String args[]) {
Punch punch = new Punch();
Atom atom = new Atom();

atom.setPunch(punch);
atom.attack();
}
}


실행 결과는 다음과 같다.

3. 문제점


위 소스코드는 Atom이라는 로봇이 Punch공격을 하는 소스코드이다.

 

만약 다음과 같은 요구사항의 변경이 발생했다고 가정해보자.

 - 로봇의 종류는 Atom뿐만 아니라, MazingerZ, TaekwonV가 추가된다.

 - 공격의 종류는 Punch뿐만 아니라, Missle, Kick이 추가된다.


첫 번째 요구사항을 충족하기 위해서는, MazingerZ, TaekwonV라는 로봇클래스가 추가되어야 한다. 


두 번째 요구사항을 충족하기 위해서는, 로봇들과 마찬가지로 Missile, Kick 클래스가 추가되어야 한다.


세 번째 요구사항을 충족하기 위해서는, NaiveClient의 ADriver객체를 BDriver객체로 바꾸어야 한다. 또한, Taxi의 ADriver 타입을 BDriver타입으로 변경해야한다.


문제는, 이러한 사항은 요구사항이 변경될 때 마다 계속해서 바뀐다는 것이다.



4. 해결방안


위와 같은 문제는, 2가지에 의해 발생한다.


1. Client에서 Atom이라는 구체적인 타입에 대한 객체를 사용한다.

2. Atom에서 Punch라는 구체적인 타입에 대한 객체를 사용한다.


위 두가지는 모두 '결합도가 높다'는 것을 의미한다.


따라서, 결합도를 낮추어 위와 같은 문제가 발생했을 때, 기존 소스코드의 변경을 최소화해야 한다.


Atom, MazingerZ, TaekwonV는 공통적으로 로봇이라고 칭할 수 있다. 따라서, Robot이라는 추상클래스를 통해 추상화하면 된다.


이렇게 한다면, 어떤 종류의 로봇이 오든, Client는 알 필요가 없다.


Punch, Missile, Kick 역시 마찬가지이다. Attack이라 추상적인 개념을 만들고 다루어야 한다.


이렇게 한다면 결합도가 낮아지기 때문에, 어떤 Robot이 오든, 어떤 Attack이 오든 상관없어진다.



5. Solution Code


 - Implementor

public interface Implementor {
public void implementation();
}

구현부에 대한 인터페이스이다. 예제에서는 Attack에 해당한다.


 - ConcreteImplementor1, 2

public class ConcreteImplementor1 implements Implementor {

@Override
public void implementation() {
System.out.println("ConcreteImplementor1# implementation()");
}
}

public class ConcreteImplementor2 implements Implementor {

@Override
public void implementation() {
System.out.println("ConcreteImplementor2# implementation()");
}
}


구체적인 구현부이다. 인터페이스를 통해 이를 사용하는 Abstraction과의 결합도가 감소한다.

예제에서는 Punch, Missile, Kick에 해당한다.


 - Abstraction

public abstract class Abstraction {
protected Implementor implementor;

public void setImplementor(Implementor implementor) {
this.implementor = implementor;
}

public abstract void doAbstraction();
}

추상적인 개념이다. 예제에서는 Robot에 해당한다.


- RefinedAbstraction

public class RefinedAbstraction1 extends Abstraction {
@Override
public void doAbstraction() {
System.out.println("RefinedAbstraction1# doAbstraction()");
this.implementor.implementation();
}
}

public class RefinedAbstraction2 extends Abstraction {
@Override
public void doAbstraction() {
System.out.println("RefinedAbstraction2# doAbstraction()");
this.implementor.implementation();
}
}

구체적인 로봇 클래스이다. 예제에서는 Atom, MazingerZ, TaekwonV에 해당한다.


 - Client

public class Client {
public static void main(String args[]) {
Abstraction abstraction1 = new RefinedAbstraction1();
Implementor implementor1 = new ConcreteImplementor1();
abstraction1.setImplementor(implementor1);
abstraction1.doAbstraction();


Abstraction abstraction2 = new RefinedAbstraction2();
Implementor implementor2 = new ConcreteImplementor2();
abstraction2.setImplementor(implementor2);
abstraction2.doAbstraction();
}
}


실행 결과는 다음과 같다.


6. Bridge Pattern


Bridge Pattern은 구현부와 추상층을 분리하여 클래스간의 결합도를 낮추고, 다형성을 구현하도록 한다.


Bridge Pattern을 적용하기 전의 클래스 호출의 방향성은


Client -> Concrete Class -> Concrete Class 의 순서였다.



여기에 Bridge Pattern을 적용한 후에는


Client -> Abstract Class -> Concerete Class -> Interface Class -> Implementation Class


와 같은 흐름이 될 것이다.



Abstract Class, Interface Class등 추상적인 개념은 일종의 중간다리 역할을 하는 것이다.


OOP에서 '다형성'을 구현하기 위한 기본적인 패턴이다.


Bridge Pattern과 Strategy Pattern은 Class Diagram, 즉 구현 자체는 거의 일치하지만, '의도'가 다르다.


Bridge Pattern

 - 구현부와 추상층으로 분리하여 서로가 다르게 동작할 수 있게 한다.

 - 상속계층에 인터페이스를 두고, 이 상속계층에 구현부를 포함시킨다.

 - Structural Pattern이다.

 - 추상화와 구현이 컴파일 타임에 결정되지 않을 때 사용

 - 추상화 및 구현은 독립적으로 변경되어야 할 때 사용

 - 추상화 구현 변경으로 인해 Client에 영향을 미치지 않아야 할 때 사용

 - Client는 구현 세부 사항으로부터 격리되어야 할 때 사용


Strategy Pattern

 - 알고리즘 집단을 정의하고, 각각을 캡슐화 하여 상호교환하여 시시각각 바꿀 수 있게 한다.

 - 알고리즘과 이를 사용하는 Client의 결합도를 낮춘다.

 - 인터페이스를 통해 알고리즘을 추상화하고, 이를 구현하는 클래스에서 알고리즘을 구체화 한다.

 - Behavioral Pattern이다.

 - 여러 버전의 알고리즘이 필요할 때 사용

 - 런타임에 클래스의 동작을 동적으로 변경해야 할 때 사용

 - 조건문을 피해야 할 때 사용


어떻게 보면, Bridge Pattern은 Strategy Pattern에 포함된다고 볼 수 있다.


'행동'이라는 것은 하나의 동작을 진행할 때 런타임 시점에서 사용자가 알맞는 알고리즘등을 선택하여 진행하는 것이다.


'구조'라는 것은 인터페이스의 구조를 나누고 추상적인 참조를 통해 합치는 의미이다.



- UML & Sequence Diagram



- Class Diagram




7. 장단점


장점 :

단점 :

'Design Pattern > Structural Pattern' 카테고리의 다른 글

Flyweight Pattern  (0) 2018.02.16
Facade Pattern (기본)  (4) 2018.02.16
Adapter Pattern  (0) 2018.02.16
Proxy Pattern  (0) 2018.02.16
Composite Pattern  (0) 2017.09.26
Comments