실제 응용 프로그램에서 Bridge Pattern 을 사용한 적이 있습니까? 그렇다면 어떻게 사용 했습니까? 나입니까, 아니면 약간의 의존성 주입이 혼합 된 어댑터 패턴입니까? 정말 자체 패턴을 가질 가치가 있습니까?
실제 응용 프로그램에서 Bridge Pattern 을 사용한 적이 있습니까? 그렇다면 어떻게 사용 했습니까? 나입니까, 아니면 약간의 의존성 주입이 혼합 된 어댑터 패턴입니까? 정말 자체 패턴을 가질 가치가 있습니까?
답변:
Bridge 패턴의 전형적인 예는 UI 환경에서 모양을 정의하는 데 사용됩니다 ( Bridge pattern Wikipedia 항목 참조 ). Bridge 패턴은 템플릿 과 전략 패턴 의 합성물 입니다 .
브리지 패턴에서 어댑터 패턴의 몇 가지 측면을 공통적으로 볼 수 있습니다. 그러나이 기사 에서 인용하면 다음과 같습니다.
언뜻보기에 Bridge 패턴은 클래스가 한 종류의 인터페이스를 다른 종류로 변환하는 데 사용된다는 점에서 Adapter 패턴과 매우 유사합니다. 그러나 어댑터 패턴의 목적은 하나 이상의 클래스의 인터페이스를 특정 클래스의 인터페이스와 동일하게 만드는 것입니다. Bridge 패턴은 구현에서 클래스의 인터페이스를 분리하도록 설계되었으므로 클라이언트 코드를 변경하지 않고도 구현을 변경하거나 대체 할 수 있습니다.
Bridge 패턴은 "상속보다 컴포지션 선호"라는 오래된 조언을 적용한 것입니다. 서로 직교하는 방식으로 서로 다른 시간을 하위 클래스 화해야 할 때 편리합니다. 컬러 모양의 계층을 구현해야한다고 가정 해 보겠습니다. Rectangle과 Circle을 사용하여 Shape를 하위 클래스로 분류 한 다음 RedRectangle, BlueRectangle 및 GreenRectangle을 사용하여 Rectangle을 하위 클래스로 만들고 Circle에 대해서도 동일하게 하위 클래스를 만들지 않겠습니까? 각 모양 에는 색상 이 있고 색상 계층 구조를 구현하는 것이 좋습니다. 이것이 바로 브릿지 패턴입니다. 글쎄요, 저는 "색상 계층 구조"를 구현하지 않겠지 만 당신은 아이디어를 얻었습니다.
언제:
A
/ \
Aa Ab
/ \ / \
Aa1 Aa2 Ab1 Ab2
리팩터링 :
A N
/ \ / \
Aa(N) Ab(N) 1 2
어댑터와 브리지는 확실히 관련이 있으며 그 차이는 미묘합니다. 이러한 패턴 중 하나를 사용하고 있다고 생각하는 일부 사람들은 실제로 다른 패턴을 사용하고있을 가능성이 있습니다.
내가 본 설명은 이미 존재 하는 일부 호환되지 않는 클래스의 인터페이스를 통합하려고 할 때 Adapter가 사용된다는 것 입니다. 어댑터는 레거시 로 간주 될 수있는 구현에 대한 일종의 변환기 역할을합니다 .
브리지 패턴은 그린 필드 일 가능성이 더 높은 코드에 사용됩니다. 변경해야하는 구현에 대한 추상 인터페이스를 제공하기 위해 Bridge를 디자인하고 있지만 해당 구현 클래스의 인터페이스도 정의합니다.
장치 드라이버는 Bridge의 자주 인용되는 예이지만 장치 공급 업체에 대한 인터페이스 사양을 정의하는 경우 Bridge라고 말하고 싶지만 기존 장치 드라이버를 가져 와서 래퍼 클래스를 만드는 경우 어댑터입니다. 통합 인터페이스를 제공합니다.
따라서 코드 측면에서 두 패턴은 매우 유사합니다. 비즈니스 측면에서는 다릅니다.
Bridge 와 Adapter 의 의도 는 다르며 두 패턴이 별도로 필요합니다.
브리지 패턴 :
다음과 같은 경우 브리지 패턴을 사용합니다.
@ John Sonmez 답변은 클래스 계층 구조를 줄이는 데 브리지 패턴의 효과를 명확하게 보여줍니다.
아래 문서 링크를 참조하여 코드 예제로 브리지 패턴에 대한 더 나은 통찰력을 얻을 수 있습니다.
어댑터 패턴 :
주요 차이점 :
UML 다이어그램 및 작업 코드와 관련된 SE 질문 :
유용한 기사 :
sourcemaking 브리지 패턴 기사
sourcemaking 어댑터 패턴 기사
journaldev 브리지 패턴 기사
편집하다:
Bridge Pattern 실제 사례
브리지 패턴은 구현에서 추상화를 분리하여 둘 다 독립적으로 달라질 수 있습니다. 그것은 상속이 아닌 구성으로 달성되었습니다.
Wikipedia의 브리지 패턴 UML :
이 패턴에는 네 가지 구성 요소가 있습니다.
Abstraction
: 인터페이스를 정의합니다.
RefinedAbstraction
: 추상화를 구현합니다.
Implementor
: 구현을위한 인터페이스를 정의합니다.
ConcreteImplementor
: Implementor 인터페이스를 구현합니다.
The crux of Bridge pattern :
컴포지션을 사용하는 두 개의 직교 클래스 계층 (상속 없음). 추상화 계층 및 구현 계층은 독립적으로 다를 수 있습니다. 구현은 추상화를 참조하지 않습니다. 추상화에는 구성을 통해 구현 인터페이스가 구성원으로 포함됩니다. 이 구성은 상속 계층의 수준을 한 단계 더 줄입니다.
실제 단어 사용 사례 :
다른 차량이 수동 및 자동 기어 시스템의 두 버전을 모두 갖도록합니다.
예제 코드 :
/* Implementor interface*/
interface Gear{
void handleGear();
}
/* Concrete Implementor - 1 */
class ManualGear implements Gear{
public void handleGear(){
System.out.println("Manual gear");
}
}
/* Concrete Implementor - 2 */
class AutoGear implements Gear{
public void handleGear(){
System.out.println("Auto gear");
}
}
/* Abstraction (abstract class) */
abstract class Vehicle {
Gear gear;
public Vehicle(Gear gear){
this.gear = gear;
}
abstract void addGear();
}
/* RefinedAbstraction - 1*/
class Car extends Vehicle{
public Car(Gear gear){
super(gear);
// initialize various other Car components to make the car
}
public void addGear(){
System.out.print("Car handles ");
gear.handleGear();
}
}
/* RefinedAbstraction - 2 */
class Truck extends Vehicle{
public Truck(Gear gear){
super(gear);
// initialize various other Truck components to make the car
}
public void addGear(){
System.out.print("Truck handles " );
gear.handleGear();
}
}
/* Client program */
public class BridgeDemo {
public static void main(String args[]){
Gear gear = new ManualGear();
Vehicle vehicle = new Car(gear);
vehicle.addGear();
gear = new AutoGear();
vehicle = new Car(gear);
vehicle.addGear();
gear = new ManualGear();
vehicle = new Truck(gear);
vehicle.addGear();
gear = new AutoGear();
vehicle = new Truck(gear);
vehicle.addGear();
}
}
산출:
Car handles Manual gear
Car handles Auto gear
Truck handles Manual gear
Truck handles Auto gear
설명:
Vehicle
추상화입니다. Car
의 Truck
두 가지 구체적인 구현입니다 Vehicle
.Vehicle
추상적 인 방법을 정의합니다 : addGear()
.Gear
구현 자 인터페이스ManualGear
그리고 AutoGear
두 가지 구현입니다 Gear
Vehicle
implementor
인터페이스를 구현하는 대신 인터페이스를 포함 합니다. Compositon
구현 자 인터페이스의 핵심은이 패턴의 핵심입니다. 추상화와 구현이 독립적으로 달라질 수 있습니다. Car
및 Truck
추상화를위한 구현 (재정의 된 추상화)을 정의합니다. addGear()
: : 포함 Gear
-둘 중 하나 Manual
또는Auto
브리지 패턴의 사용 사례 :
나는 직장에서 브리지 패턴을 사용했습니다. 필자는 종종 PIMPL 관용구 (구현 포인터)라고하는 C ++로 프로그래밍합니다. 다음과 같이 보입니다.
class A
{
public:
void foo()
{
pImpl->foo();
}
private:
Aimpl *pImpl;
};
class Aimpl
{
public:
void foo();
void bar();
};
이 예 class A
에는 인터페이스가 포함되어 있습니다.class Aimpl
포함하고 구현을 포함합니다.
이 패턴의 한 가지 용도는 구현 클래스의 공용 멤버 중 일부만 노출하고 나머지는 노출하지 않는 것입니다. 이 예제에서는 Aimpl::foo()
공용 인터페이스를 통해서만 호출 할 수 있습니다.A
있지만Aimpl::bar()
또 다른 이점은 Aimpl
사용자가 포함 할 필요가없는 별도의 헤더 파일에서 정의 할 수 있다는 것 입니다 A
. 당신이해야 할 일은 Aimpl
before A
is defined 의 정방향 선언을 사용하고 참조하는 모든 멤버 함수의 정의를 pImpl
.cpp 파일로 옮기는 것 입니다. 이를 통해 Aimpl
헤더를 비공개 로 유지 하고 컴파일 시간을 줄일 수 있습니다.
코드에 모양 예제를 넣으려면 :
#include<iostream>
#include<string>
#include<cstdlib>
using namespace std;
class IColor
{
public:
virtual string Color() = 0;
};
class RedColor: public IColor
{
public:
string Color()
{
return "of Red Color";
}
};
class BlueColor: public IColor
{
public:
string Color()
{
return "of Blue Color";
}
};
class IShape
{
public:
virtual string Draw() = 0;
};
class Circle: public IShape
{
IColor* impl;
public:
Circle(IColor *obj):impl(obj){}
string Draw()
{
return "Drawn a Circle "+ impl->Color();
}
};
class Square: public IShape
{
IColor* impl;
public:
Square(IColor *obj):impl(obj){}
string Draw()
{
return "Drawn a Square "+ impl->Color();;
}
};
int main()
{
IColor* red = new RedColor();
IColor* blue = new BlueColor();
IShape* sq = new Square(red);
IShape* cr = new Circle(blue);
cout<<"\n"<<sq->Draw();
cout<<"\n"<<cr->Draw();
delete red;
delete blue;
return 1;
}
출력은 다음과 같습니다.
Drawn a Square of Red Color
Drawn a Circle of Blue Color
순열로 인해 하위 클래스가 폭발적으로 증가하지 않고 시스템에 새로운 색상과 모양을 쉽게 추가 할 수 있습니다.
회계, 계약, 청구 등 다양한 종류의 작업을 관리하는 워크 플로 응용 프로그램을 개발하는 보험 회사에서 일하고 있습니다. 이것이 추상화입니다. 구현 측면에서 전자 메일, 팩스, 전자 메시지 등 다양한 소스에서 작업을 생성 할 수 있어야합니다.
다음 클래스로 디자인을 시작합니다.
public class Task {...}
public class AccountingTask : Task {...}
public class ContractTask : Task {...}
public class ClaimTask : Task {...}
이제 각 소스를 특정 방식으로 처리해야하므로 각 작업 유형을 전문화하기로 결정합니다.
public class EmailAccountingTask : AccountingTask {...}
public class FaxAccountingTask : AccountingTask {...}
public class EmessagingAccountingTask : AccountingTask {...}
public class EmailContractTask : ContractTask {...}
public class FaxContractTask : ContractTask {...}
public class EmessagingContractTask : ContractTask {...}
public class EmailClaimTask : ClaimTask {...}
public class FaxClaimTask : ClaimTask {...}
public class EmessagingClaimTask : ClaimTask {...}
13 개의 수업으로 끝납니다. 작업 유형 또는 소스 유형을 추가하는 것이 어려워집니다. 브리지 패턴을 사용하면 작업 (추상화)을 소스 (구현 문제)에서 분리하여 유지 관리하기가 더 쉽습니다.
// Source
public class Source {
public string GetSender();
public string GetMessage();
public string GetContractReference();
(...)
}
public class EmailSource : Source {...}
public class FaxSource : Source {...}
public class EmessagingSource : Source {...}
// Task
public class Task {
public Task(Source source);
(...)
}
public class AccountingTask : Task {...}
public class ContractTask : Task {...}
public class ClaimTask : Task {...}
이제 작업 유형 또는 소스를 추가하는 것이 훨씬 더 쉬워졌습니다.
참고 : 대부분의 개발자는이 문제를 처리하기 위해 13 개의 클래스 계층 구조를 미리 만들지 않습니다. 그러나 실제 생활에서는 소스 및 작업 유형의 수를 미리 알지 못할 수 있습니다. 소스가 하나만 있고 작업 유형이 두 개이면 소스에서 작업을 분리하지 않을 것입니다. 그런 다음 새로운 소스와 작업 유형이 추가됨에 따라 전반적인 복잡성이 증가합니다. 언젠가는 리팩토링을하게되고 대부분의 경우 브리지와 같은 솔루션으로 끝납니다.
Bridge design pattern we can easily understand helping of service and dao layer.
Dao layer -> create common interface for dao layer ->
public interface Dao<T>{
void save(T t);
}
public class AccountDao<Account> implement Dao<Account>{
public void save(Account){
}
}
public LoginDao<Login> implement Dao<Login>{
public void save(Login){
}
}
Service Layer ->
1) interface
public interface BasicService<T>{
void save(T t);
}
concrete implementation of service -
Account service -
public class AccountService<Account> implement BasicService<Account>{
private Dao<Account> accountDao;
public AccountService(AccountDao dao){
this.accountDao=dao;
}
public void save(Account){
accountDao.save(Account);
}
}
login service-
public class LoginService<Login> implement BasicService<Login>{
private Dao<Login> loginDao;
public AccountService(LoginDao dao){
this.loginDao=dao;
}
public void save(Login){
loginDao.save(login);
}
}
public class BridgePattenDemo{
public static void main(String[] str){
BasicService<Account> aService=new AccountService(new AccountDao<Account>());
Account ac=new Account();
aService.save(ac);
}
}
}