현대 C 언어에서의 교량 설계 모델++

브리지 디자인 모델은 하나의 구조 디자인 모델로 클래스를 두 부분으로 결합시키는 데 사용된다. 추상과 실현이다. 둘 다 독립적으로 개발할 수 있도록 한다.이것은 유추상과 그 실현 간의 느슨한 결합을 촉진시켰다.더 높은 단계의 간접 주소 찾기, 즉 원시 클래스와 기능 간의 교량 인터페이스를 추가함으로써 이러한 결합을 실현할 수 있다.절연은 C++ 세계에서 교량 설계 모델의 또 다른 이름이다.

"All problems in computer science can be solved by another level of indirection." -- David Wheeler


참고로 구조 설계 모델에 대한 나의 다른 글을 읽지 않았다면 다음은 목록입니다.
  • Adapter
  • Bridge
  • Composite
  • Decorator
  • Facade
  • Flyweight
  • Proxy
  • 이 시리즈에서 보신 코드 단편은 복잡한 것이 아니라 간소화된 것입니다.그래서 너는 내가 override, final, public 같은 키워드를 사용하지 않는 것을 자주 본다. 단지 하나의 표준 화면 크기에서 코드를 치밀하고 소비할 수 있도록 하기 위해서이다.나도 struct 가 아니라 class 을 더 좋아한다. 다만 때때로 "public:"를 쓰지 않고 줄을 저장하고, 일부러 virtual destructor, 구조 함수, copy constructor, 접두사std::를 놓치고, 동적 메모리를 삭제한다.나는 또 내가 실용적인 사람이라고 생각하며, 가능한 한 간단한 방식으로 하나의 생각을 표현하기를 바란다. 표준적인 방식이나 행어가 아니라.
    주:
  • 만약 네가 여기서 직접 걸려 넘어진다면, 나는 네가 먼저 What is design pattern?를 통과할 것을 건의한다. 설령 그것이 매우 작다 하더라도.나는 이것이 너희들이 이 화제에서 더욱 많은 탐색을 하도록 격려할 것이라고 믿는다.
  • 이 시리즈에서 본 모든 코드는 C++20으로 컴파일되었습니다. 대부분의 경우 Modern C++ C++17 이전의 기능을 사용했지만요.따라서 최신 컴파일러에 접근할 수 있는 권한이 없으면 https://wandbox.org/ 을 사용할 수 있으며, boost 라이브러리도 미리 설치되어 있습니다.
  • /!\: This article has been originally published on my blog. If you are interested in receiving my latest articles, please sign up to my newsletter.


    의도


    To separate the interface from its implementation.

  • 다시 말하면 이 모든 것은 계승/범주화가 아닌 집합/조합을 통해 구성 요소를 연결하는 데 관한 것이다.
  • 이 모델은 교량을 충당하는 인터페이스와 관련된다.이것은 구체적인 유형의 기능을 인터페이스 실현자 유형에 독립시켰다.이 두 종류의 종류는 서로 영향을 주지 않고 구조적으로 변경할 수 있다.
  • 교량 설계 모델의 동기

  • 브리지 디자인 모델은 피리칼 제품의 복잡성 폭발을 방지한다.이 수학 용어를 두려워하지 마라. 나는 아래의 한 예로 그것을 간소화했다.
  • 예를 들어 Shape라는 기본 클래스가 있다고 가정하면 Shape 또는 Circle 또는 API 1 또는 API 2로 그릴 수 있습니다.
  • struct DrawingAPI_1 { };
    struct DrawingAPI_2 { };
    
    struct Shape { virtual void draw() = 0; };
    
    /* 2 x 2 scenario */
    struct Circle : Shape, DrawingAPI_1 { };
    struct Circle : Shape, DrawingAPI_2 { };
    
    struct Square : Shape, DrawingAPI_1 { };
    struct Square : Shape, DrawingAPI_2 { };
    
  • 이렇게 하면 최종적으로 2 곱하기 2(2×2) 장면을 얻을 수 있다.따라서 만약 당신이 그것을 실현하기로 결정한다면, 당신은 반드시 네 가지 유형을 실현해야 한다.하나는SquareCircle,API_1Circle 등등.
  • 교량 설계 모델은 바로 전체 실체의 폭발을 피하는 모델이다.
  • 따라서 위와 같이 Drawing API 인터페이스를 설계하고 (이후 파생 API 1과 2에 사용) 이를 API_2Circle 에 집합할 필요가 없습니다.
  • 교량 설계 모드 C++ 예

  • 다음은 교량 설계 모델의 전형적인 실현이다.우리는 여기서 이렇게 복잡한 일을 토론할 생각은 없다.그러나 본질적으로 브리지 연결은 인터페이스나 차원 구조와 결합을 실현하는 메커니즘이다.
  • struct DrawingAPI {
        virtual void drawCircle() = 0;
    };
    
    struct DrawingAPI_1 : DrawingAPI {
        void drawCircle() { cout << "Drawn by API 1"<< endl; }
    };
    
    struct DrawingAPI_2 : DrawingAPI {
        void drawCircle() { cout << "Drawn by API 2"<< endl; }
    };
    
    struct Shape {
        Shape(DrawingAPI &drawingAPI) : m_drawingAPI{drawingAPI} {}
        virtual void draw() = 0;
    protected:
        DrawingAPI &m_drawingAPI;   // Now Shapes does not need to worry about drawing APIs
    };
    
    struct Circle : Shape {
        Circle(DrawingAPI &drawingAPI) : Shape{drawingAPI} {}
        void draw() { m_drawingAPI.drawCircle(); }
    };
    
    int main() {
        DrawingAPI_1 API_1;
        DrawingAPI_2 API_2;
        Circle(API_1).draw();
        Circle(API_2).draw();
        return EXIT_SUCCESS;
    }
    
  • 이렇게 하면 당신은 계승과 집합에 의존하는 것처럼 의존하지 않을 것입니다.반대로 당신은 인터페이스에 의존합니다.
  • C++ 관용법을 사용하는 브리지 설계 모드: PIMPL

  • 교량 설계 모델을 토론할 때 우리가'여드름'이라는 성어를 어떻게 잊을 수 있겠는가!'덩어리'는 교량 설계 모델의 표현 형식인데, 비록 약간 다르지만.
  • PIMPL 습어는 특정한 종류의 실현 세부 사항을 숨기는 데 관한 것으로 방법은 바늘이 가리키는 단독 실현에 붙이는 것이다. 말 그대로이다.내가 너에게 그것이 어떻게 작동하는지 알려줄게.
  • 사람h
    #pragma once
    #include <string>
    #include <memory>
    
    struct Person {
        /* PIMPL ------------------------------------ */
        class PersonImpl;
        unique_ptr<PersonImpl>  m_impl; // bridge - not necessarily inner class, can vary
        /* ------------------------------------------ */
        string                  m_name;
    
        Person();
        ~Person();
    
        void greet();
    private:
        // secret data members or methods are in `PersonImpl` not here
        // as we are going to expose this class to client
    };
    
    사람cpp<-- 비즈니스 논리를 숨기기 위해 공유 라이브러리(.so/.dll)로 변환
    #include "Person.h"
    
    /* PIMPL Implementation ------------------------------------ */
    struct Person::PersonImpl {
        void greet(Person *p) {
            cout << "hello "<< p->name.c_str() << endl;
        }
    };
    /* --------------------------------------------------------- */
    
    Person::Person() : m_impl(new PersonImpl) { }
    Person::~Person() { delete m_impl; }
    void Person::greet() { m_impl->greet(this); }
    
  • 좋습니다. 이것은 여드름 습어입니다. 우리는 그 형식이 매우 간결하다고 말할 수 있습니다.문제는 니가 왜 이러는 거야?
  • 보안 목적으로 클래스 API를 포함하는 클라이언트에게 헤더 파일을 어떤 방식으로 공개해야 하는지 질문이 있을 수 있습니다. 그러면 여기서 어떻게 보안을 얻을 수 있습니까?그래, 데이터 구성원과 개인 방법을 생각해 보자.만약 당신에게 비즈니스 비밀이 있다면, 중요한 정보를 포함하는 데이터 구성원이 있습니다.당신은 왜 고객에게 object의 이름을 알려야 합니까?
  • PIMPL의 또 다른 장점은 컴파일 시간이다. 이것은 C++에 있어서 매우 중요하다. 왜냐하면 그것은 광범위한 비판을 받았기 때문이다.그러나 컴파일러가 점점 증량화됨에 따라 이 점은 점점 중요하지 않게 변했다.지금 그들은 정말 대단하다.
  • 안전하고 빠른 PIMPL

  • 간접 주소를 사용하는 모든 API를 사용해야 하기 때문에 매번 인용 포인터의 접근을 취소해야 하기 때문에 일부 실행 시 비용이 발생합니다.
  • 그 밖에 우리는 std::unique_ptr의 구조와 폐기 비용을 가지고 있다. 왜냐하면 그것은 더미 속에 메모리를 만들었기 때문이다. 그 중에서 많은 다른 함수 호출과 시스템 호출과 관련이 있기 때문이다.
  • 그 밖에 만약에 우리가 SquarePerson의 데이터 구성원, 예를 들어 전달PersonImpl 지침 등을 방문하고 싶다면 우리는 반드시 간접적인 조작을 해야 한다.
  • 사람h
    #pragma once
    #include <string>
    #include <cstddef>
    #include <type_traits>
    
    struct Person {
        Person();
        ~Person();
        void greet();
    
    private:
        static constexpr size_t     m_size = 1024;
        using pimpl_storage_t = aligned_storage<m_size, alignment_of_v<max_align_t>>::type;
    
        string                      m_name;
        pimpl_storage_t             m_impl;
    };
    
    사람cpp<-- 비즈니스 논리를 숨기기 위해 공유 라이브러리(.so/.dll)로 변환
    #include "Person.h"
    #include <iostream>
    
    struct PersonImpl {
        void greet(string &name) {
            cout << "hello "<< name << endl;
        }
    };
    
    Person::Person() {
      static_assert(sizeof(impl) >= sizeof(PersonImpl)); // Compile time safety
      new(&impl) PersonImpl;
    }
    Person::~Person() { reinterpret_cast<PersonImpl*>(&impl)->~PersonImpl(); }
    void Person::greet() { reinterpret_cast<PersonImpl*>(&impl)->greet(name);  }
    
  • 새로운 연산자와 미리 분배된 메모리 버퍼를 배치함으로써 이 문제를 해결합시다.std::unique_ptr 컴파일할 때만 바뀌기 때문에 다른 간접 주소는 없습니다.
  • 캐스트 재해석 교량 설계 모델의 장점

  • 브리지 디자인 모델은 독립적으로 추상(즉 인터페이스)을 개발하고 실현하는 유연성을 제공했다.클라이언트/API 사용자 코드는 추상적인 부분만 접근할 수 있을 뿐 구현 부분에 관심을 가질 필요가 없습니다.
  • 보존 , 다시 말하면 확장성을 향상시켰다. 클라이언트/API 사용자 코드는 추상성에 의존하기 때문에 언제든지 수정하거나 확장할 수 있다.
  • , 사용Open-Closed Principle 형식의 교량 설계 모델.우리는 상기 PIMPL 습관 용법 예시에서 말한 바와 같이 클라이언트에게 세부 사항을 숨길 수 있다.
  • 브리지 디자인 모델은 낡은 건의에 대한 응용으로'계승이 아니라 조합을 선호하지만 더욱 스마트한 방식을 채택했다.서로 정교한 방식으로 서로 다른 시간에 대해 하위 분류를 해야 할 때 (예를 들어 앞에서 논의한 2x2 문제) 매우 편리하다.
  • 추상적이고 그 실현 사이의 번역을 피할 때 귀속을 피해야 한다.실행할 때 선택할 수 있도록 합니다.
  • PIMPL 회사 FAQ 요약


    교량 설계 모델의 실제 용례는 무엇입니까?
    모든 인터넷 브라우저의 플러그인은 이런 모델을 직접 이용하는데 그 중에서 브라우저는 추상적인 것만 지정하고 서로 다른 유형의 플러그인에 따라 다르게 실현한다.
    교량 설계 모델은 언제 사용합니까?
    - 구현 또는 변형이 확실하지 않을 때 계속 개발하기를 원합니다.
    - 행동 교환 문제, 즉 피리칼 곱셈 복잡성 폭발.
    어댑터와 브리지 디자인 모델 사이에는 어떤 차이가 있습니까?
    --Adapter는 일반적으로 기존 응용 프로그램과 함께 사용되어 다른 방면에서 호환되지 않는 클래스들이 협동하여 작업할 수 있도록 한다.
    --Bridge는 일반적으로 미리 설계되어 응용 프로그램의 각 부분을 독립적으로 개발할 수 있습니다.
    전략과 교량 설계 모델 사이에는 어떤 차이가 있습니까?
    - 다중 드라이버와 유사한 1차원 문제입니다.
    - 브리지는 통신 유형과 장치와 유사한 다차원 문제입니다.Strategy

    좋은 웹페이지 즐겨찾기