Go 언어는 GoF 디자인 패턴을 구현합니다: 어댑터 패턴

이 기사는 Huawei Cloud 커뮤니티 " [Go 구현] GoF 연습을 위한 23가지 디자인 패턴: 어댑터 패턴 ", 저자: Yuan Runzi에서 공유되었습니다.

소개

어댑터 모드는 가장 일반적으로 사용되는 구조 모드 중 하나입니다.실생활에서 어댑터 모드는 영국 플러그가 중국 소켓에서 작동할 수 있도록 하는 전원 플러그 변환기와 같은 모든 곳에서 볼 수 있습니다.

GoF는 이를 다음과 같이 정의합니다.

클래스의 인터페이스를 클라이언트가 기대하는 다른 인터페이스로 변환합니다. 어댑터를 사용하면 호환되지 않는 인터페이스로 인해 불가능했던 클래스가 함께 작동할 수 있습니다.

간단히 말해서 어댑터 패턴을 사용하면 일치하지 않는 인터페이스로 인해 원래는 함께 작동할 수 없었던 두 클래스/구조가 함께 작동할 수 있습니다 .

어댑터 패턴이 하는 일은 인터페이스를 어댑터를 통해 클라이언트가 기대하는 다른 인터페이스  Adaptee Adapter  Target  변환하는 것이고 , 구현 원리도 인터페이스를 구현 하고 해당 메소드에서 인터페이스를 호출하면 매우 간단합니다. Adapter  Target  Adaptee 

UML 구조

장면 컨텍스트

간단한 분산 응용 시스템 (샘플 코드 프로젝트) 에서 db 모듈은 서비스 등록 정보 및 시스템 모니터링 데이터를 저장하는 데 사용되는 키-값 데이터베이스입니다. 방문자 모드 에서는 테이블 열 쿼리 기능을 구현했으며 동시에 간단한 SQL 쿼리 함수도 구현했습니다(인터프리터 모드 에서 소개될 예정입니다 ). 쿼리 결과는 다음과 같은 구조입니다. 결과 .      SqlResult  toMap  map 

사용자의 편의를 위해 터미널 콘솔에서 인간-컴퓨터 상호 작용을 제공하는 기능을 구현합니다.아래와 같이 사용자가 SQL 문을 입력하면 쿼리 결과가 백그라운드에서 반환됩니다.

터미널 콘솔의 구체적인 구현은 확장 가능한 쿼리 결과 표시 스타일을 제공하기 위해 인터페이스를 설계했지만 이 인터페이스가 구현되지 않았기 때문에 쿼리 결과를 직접 렌더링할 수 없다는 것입니다 . Console ConsoleRender  SqlResult  Console  SqlResult 

이를 위해서는 쿼리 결과가 어댑터를 통해 렌더링될 수 있도록 어댑터를 구현해야 합니다. 예제에서는 위와 같이 인터페이스를 구현 하고 쿼리 결과를 테이블 형식으로 렌더링하는 어댑터를 설계했습니다. Console  SqlResult  TableRender ConsoleRender 

암호

// 데모/db/sql.go
패키지 DB

//Adaptee SQL 문 실행으로 반환된 결과는 Target 인터페이스를 구현하지 않습니다.
SqlResult 구조체를 입력하세요.
    필드 []문자열
    vals []인터페이스{}
}

func (s *SqlResult) Add(필드 문자열, 레코드 인터페이스{}) {
    s.fields = 추가(s.fields, 필드)
    s.vals = 추가(s.vals, 레코드)
}

func (s *SqlResult) ToMap() 지도[문자열]인터페이스{} {
    결과 := make(map[string]인터페이스{})
    i, f := 범위 s.fields {
        결과[f] = s.vals[i]
    }
    결과 반환
}

// 데모/db/console.go
패키지 DB

// 클라이언트 터미널 콘솔
콘솔 구조체를 입력하세요.
    DB DB
}

// 출력은 ConsoleRender를 호출하여 쿼리 결과의 렌더링 출력을 완료합니다.
func (c *Console) Output(렌더링 ConsoleRender) {
    fmt.Println(렌더링.렌더())
}

// 대상 인터페이스, 콘솔 db 쿼리 결과 렌더링 인터페이스
ConsoleRender 인터페이스 유형 {
    렌더() 문자열
}

// TableRender 쿼리 결과 렌더링 어댑터를 표 형식으로 표시
// 요점 1: Adapter 구조/클래스 정의
TableRender 구조체 유형 {
    // 핵심 사항 2: Adapter의 Aggregate Adaptee, 여기서 SqlResult는 TableRender의 멤버 변수로 사용됩니다.
    결과 *SQLResult
}

// 핵심 포인트 3: Target 인터페이스를 구현합니다. 여기에는 ConsoleRender 인터페이스가 있습니다.
func (t *TableRender) Render() 문자열 {
    // 요점 4: Target 인터페이스 구현에서 Adaptee의 원래 메서드를 호출하여 특정 비즈니스 로직을 구현합니다.
    vals := t.result.ToMap()
    var 헤더 []문자열
    var 데이터 [] 문자열
    키의 경우 val := 범위 vals {
        헤더 = 추가(헤더, 키)
        데이터 = 추가(데이터, fmt.Sprintf("%v", val))
    }
    빌더 := &strings.Builder{}
    테이블 := tablewriter.NewWriter(빌더)
    테이블.SetHeader(헤더)
    테이블.추가(데이터)
    테이블.렌더링()
    빌더.문자열()을 반환합니다.
}

// 렌더링 오류 기능을 구현하는 또 다른 어댑터는 다음과 같습니다.
ErrorRender 구조체 유형 {
    오류 오류
}

func (e *ErrorRender) Render() 문자열 {
    e.err.Error()를 반환합니다.
}

클라이언트는 다음을 사용합니다.

func (c *콘솔) 시작() {
    fmt.Println("Demo DB에 오신 것을 환영합니다. 종료하려면 종료를 입력하세요!")
    fmt.Println("> SQL 표현식을 입력하세요:")
    fmt.Print("> ")
    스캐너 := bufio.NewScanner(os.Stdin)
    scanner.Scan()의 경우 {
        sql := 스캐너.텍스트()
        sql == "exit"인 경우 {
            부서지다
        }
        결과, 오류 := c.db.ExecSql(sql)
        오류 == nil인 경우 {
            // 요점 5: Target 인터페이스가 필요한 경우 어댑터 Adapter 인스턴스를 전달하고, Adapter 인스턴스 생성 시 Adaptee 인스턴스를 전달해야 합니다.
            c.Output(NewTableRender(결과))
        } 또 다른 {
            c.출력(NewErrorRender(err))
        }
        fmt.Println("> SQL 표현식을 입력하세요:")
        fmt.Print("> ")
    }
}

ConsoleRenderTarget 인터페이스( )와 Adaptee( )가 이미 있다는 전제하에 SqlResult어댑터 패턴 구현에 대한 몇 가지 핵심 사항을 요약해 보겠습니다.

  1. 어댑터 구조/클래스를 정의하십시오. 여기에 구조가 있습니다. TableRender 
  2. Adapter의 Aggregate Adaptee는 의 멤버 변수입니다 . SqlResult  TableRender 
  3. Adapter는 Target 인터페이스를 구현합니다. 여기서는 인터페이스가 구현 됩니다 . TableRender  ConsoleRender 
  4. Target 인터페이스 구현에서는 특정 비즈니스 로직을 구현하기 위해 Adaptee의 원래 메소드가 호출되는데, 여기서는 메소드를 호출하여 쿼리 결과를 얻은 다음 결과를 렌더링합니다. TableRender.Render()  SqlResult.ToMap() 
  5. 클라이언트가 Target 인터페이스를 필요로 하는 경우 Adapter 인스턴스가 전달되고 Adapter 인스턴스를 생성할 때 Adaptee 인스턴스가 전달됩니다. 여기서는 인스턴스를 생성할 때 이를 입력 매개변수로 전달한 후 해당 인스턴스를 메서드에 전달합니다. NewTableRender()  TableRender  SqlResult  TableRender  Console.Output() 

확장하다

Gin에 어댑터 패턴 적용

Gin은 고성능 웹 프레임워크로, 일반적인 사용법은 다음과 같습니다:

// 사용자 정의 요청 처리 함수, 유형은 gin.HandlerFunc
func myGinHandler(c *gin.Context) {
    ... //요청 처리를 위한 특정 논리
}

기능 메인() {
    //기본 경로 엔진을 생성합니다. 유형은 gin.Engine입니다.
    r := 진.기본값()
    //경로 정의
    r.GET("/my-route", myGinHandler)
    // 경로 엔진 시작
    r.실행()
}

실제 애플리케이션 시나리오에서는 이러한 상황이 발생할 수 있습니다. 사용자의 초기 웹 프레임워크는 Go Native를 사용했으며 사용 시나리오는 다음과 같습니다. net/http

// 사용자 정의 요청 처리 함수, http.Handler 입력
func myHttpHandler(w http.ResponseWriter, r *http.Request) {
    ... //요청 처리를 위한 특정 논리
}

기능 메인() {
    //경로 정의
    http.HandleFunc("/my-route", myHttpHandler)
    // 경로 시작
    http.ListenAndServe(":8080", nil)
}

성능 문제로 인해 현재 고객은 Gin 프레임워크로 전환을 계획하고 있으며, 당연히 myHttpHandler 인터페이스가 호환되지 않아 직접 등록할 수 없습니다 . 사용자의 편의를 위해 Gin 프레임워크에서는 유형을 유형으로 변환 할 수 있는 어댑터를 제공하며 , 이는 다음과 같이 정의됩니다. gin.Default()  gin.WrapH http.Handler  gin.HandlerFunc 

// WrapH는 http.Handler를 래핑하기 위한 도우미 함수이며 Gin 미들웨어를 반환합니다.
func WrapH(h http.Handler) HandlerFunc {
	  return func(c *컨텍스트) {
		  h.ServeHTTP(c.Writer, c.Request)
	  }
}

사용 방법:

// 사용자 정의 요청 처리 함수, http.Handler 입력
func myHttpHandler(w http.ResponseWriter, r *http.Request) {
    ... //요청 처리를 위한 특정 논리
}

기능 메인() {
    //기본 경로 엔진 생성
    r := 진.기본값()
    //경로 정의
    r.GET("/my-route", gin.WrapH(myHttpHandler))
    // 경로 엔진 시작
    r.실행()
}

이 예에서는 Target 인터페이스인 gin.Engine Client , Adapter인 Adaptee입니다. 이는 보다 간결한 대체 패턴을 사용하여 Adapter 패턴을 Go 스타일로 구현한 것입니다 .gin.HandlerFunc http.Handler gin.WrapH  func  struct

일반적인 애플리케이션 시나리오

  • 인터페이스 A를 사용자가 원하는 다른 인터페이스 B로 변환하여 원래 호환되지 않는 인터페이스 A와 인터페이스 B가 서로 협력할 수 있도록 합니다.
  • 오래된 시스템의 재구성. 원래 인터페이스를 변경하지 않고 이전 인터페이스를 새 인터페이스에 적용합니다.

장점과 단점

이점

  1. Adaptee와 Target을 분리할 수 있습니다. Target에 적응하기 위한 새로운 어댑터를 도입함으로써 Adaptee는 수정될 필요가 없으며 열기 및 닫기 원리를 준수합니다 .
  2. 유연성이 뛰어나며 다양한 어댑터를 통해 다양한 인터페이스에 쉽게 적용할 수 있습니다.

결점

  1. 코드 복잡성을 증가시킵니다. 어댑터 모드에는 새로운 어댑터가 필요하며, 남용할 경우 시스템의 코드 복잡성이 증가합니다.

다른 패턴과의 연관

어댑터 패턴은 UML 구조의 데코레이터 패턴에이전트 패턴 과 일정한 유사성을 갖습니다. 그러나 어댑터 모드는 원본 객체의 인터페이스를 변경하지만 원래 기능은 변경하지 않는 반면, 데코레이터 모드와 프록시 모드는 인터페이스를 변경하지 않고 원본 객체의 기능을 향상시킵니다.  

기사 사진

기사의 그리기 방법은 Keynote를 활용한 손그림 스타일 일러스트 에서 찾아 보실 수 있습니다 .  

참고

[1] [Go 구현] GoF 연습을 위한 23가지 디자인 패턴: SOLID 원리 , Yuan Runzi 

[2]  디자인 패턴, 4장. 구조 패턴, GoF

[3]  어댑터 모드,  refactoringguru.cn

[4] 진 웹 프레임워크 , 진 

 

화웨이 클라우드의 신기술에 대해 빨리 알아보고 팔로우하려면 클릭하세요~

 

IntelliJ IDEA 2023.3 및 JetBrains Family Bucket 연간 주요 버전 업데이트 새로운 개념 "방어 프로그래밍": 안정적인 작업 수행 GitHub.com은 1,200개 이상의 MySQL 호스트를 실행합니다. 8.0으로 원활하게 업그레이드하는 방법은 무엇입니까? Stephen Chow의 Web3 팀은 다음 달에 독립 앱을 출시할 예정입니다. Firefox는 사라질까요? Visual Studio Code 1.85 출시, 부동 창 US CISA는 메모리 보안 취약점을 제거하기 위해 C/C++ 포기를 권장합니다. Yu Chengdong: Huawei는 내년에 파괴적인 제품을 출시하고 업계 역사를 다시 쓸 것입니다. TIOBE 12월: C#이 올해의 프로그래밍 언어가 될 것으로 예상됩니다. A 논문 Lei Jun이 30년 전에 작성함: "컴퓨터 바이러스 판별 전문가 시스템의 원리 및 설계"
{{o.이름}}
{{이름}}

Acho que você gosta

Origin my.oschina.net/u/4526289/blog/10320801
Recomendado
Clasificación