함수 맵 대 스위치 명령문


21

요청을 처리하는 프로젝트를 진행 중이며 요청에는 명령과 매개 변수라는 두 가지 구성 요소가 있습니다. 각 명령의 처리기는 매우 간단합니다 (<10 줄, 종종 <5). 최소 20 개의 명령이 있으며 50 개가 넘는 명령이있을 수 있습니다.

나는 몇 가지 해결책을 생각해 냈습니다.

  • 하나의 큰 스위치 / if-else 명령
  • 명령을 함수에 매핑
  • 정적 클래스 / 단일 명령에 대한 명령 맵

각 명령은 약간의 오류 검사를 수행하며 추상화 할 수있는 유일한 비트는 각 명령에 대해 정의 된 매개 변수 수를 확인하는 것입니다.

이 문제에 대한 최선의 해결책은 무엇이며 왜 그런가? 또한 내가 놓쳤을 수도있는 모든 디자인 패턴을 사용할 수 있습니다.

나는 각각에 대해 다음과 같은 프로 / 콘 목록을 생각해 냈습니다.

스위치

  • 찬성
    • 모든 명령을 하나의 기능으로 유지합니다. 단순하기 때문에 시각적 조회 테이블이됩니다.
    • 한곳에서만 사용되는 수많은 작은 함수 / 클래스로 소스를 정리할 필요가 없습니다.
  • 단점
    • 매우 긴
    • 프로그래밍 방식으로 명령을 추가하기 어려움 (기본 사례를 사용하여 연결해야 함)

지도 명령-> 기능

  • 찬성
    • 작고 한입 크기의 덩어리
    • 프로그래밍 방식으로 명령을 추가 / 제거 할 수 있습니다
  • 단점
    • 인라인으로 수행되면 스위치와 시각적으로 동일
    • 인라인으로 수행되지 않으면 많은 기능이 한 곳에서만 사용됩니다.

맵 명령-> 정적 클래스 / 단일

  • 찬성
    • 다형성을 사용하여 간단한 오류 검사를 처리 할 수 ​​있습니다 (3 줄과 같지만 여전히)
    • map-> 함수 솔루션과 유사한 이점
  • 단점
    • 아주 작은 수업이 많으면 프로젝트가 복잡해집니다
    • 구현이 모두 같은 장소에있는 것은 아니므로 구현을 스캔하기가 쉽지 않습니다.

추가 메모 :

나는 이것을 Go로 작성하고 있지만 해결책이 언어마다 다르다고 생각하지 않습니다. 다른 언어에서 매우 비슷한 것을해야하기 때문에보다 일반적인 해결책을 찾고 있습니다.

명령은 문자열이지만 편리하다면 이것을 쉽게 숫자에 매핑 할 수 있습니다. 함수 서명은 다음과 같습니다.

Reply Command(List<String> params)

Go에는 최상위 기능이 있으며 다른 플랫폼에도 최상위 기능이 있으므로 두 번째 옵션과 세 번째 옵션의 차이점이 있습니다.


8
명령을 함수에 맵핑하고 구성에서 런타임시로드하십시오. 명령 패턴을 사용하십시오.
Steven Evers

3
많은 작은 기능을 만드는 것을 너무 두려워하지 마십시오. 일반적으로 여러 개의 작은 함수 모음은 하나의 큰 함수보다 유지 관리가 쉽습니다.
Bart van Ingen Schenau

8
사람들이 의견에 대한 질문에 답변하고 답변에 대한 자세한 정보를 요구하는이 고집이 거친 세상은 무엇입니까?
pdr

4
@SteveEvers : 정교화가 필요하지 않은 경우 아무리 짧아도 답입니다. 만약 그렇다면 시간이나 다른 것이 없다면 다른 사람이 대답 할 수 있도록 남겨 두십시오 (항상 반감기 의견이 이미있는 주석을 확인하는 답변을 작성하는 것을 속이는 것 같습니다). 개인적으로, 나는 이것이 정교화가 필요하다고 생각합니다. OP는 실제로 최고의 솔루션이 왜 최고의 솔루션인지 알고 싶어합니다.
pdr

1
@pdr-맞습니다. 내 성향은 기능에 대한 명령 맵이지만 CS 디자인 과정에서 비교적 중급 프로그래머입니다. 교수님은 많은 수업을 좋아하므로 2 가지 이상의 합법적 인 솔루션이 있습니다. 나는 지역 사회가 가장 좋아하는 것을 알고 싶었다.
beatgammit

답변:


14

이것은지도 (제 2 또는 제 3 제안 솔루션)에 매우 적합합니다. 나는 그것을 수십 번 사용해 왔으며 간단하고 효과적입니다. 나는이 솔루션들을 정말로 구별하지 못한다. 중요한 점은 기능 이름이 키인 맵이 있다는 것입니다.

필자의 의견으로는 맵 접근법의 주요 장점은 테이블이 데이터라는 것입니다. 즉, 런타임시 전달, 기능 보강 또는 달리 수정할 수 있습니다. 또한 새롭고 흥미로운 방식으로지도를 해석하는 추가 기능을 쉽게 코딩 할 수 있습니다. 케이스 / 스위치 솔루션으로는 불가능합니다.

나는 당신이 언급 한 단점을 실제로 경험하지는 않았지만 추가 단점을 언급하고 싶습니다. 문자열 이름 만 중요하면 디스패치가 쉽지만 실행할 함수를 결정하기 위해 추가 정보를 고려해야하는 경우 훨씬 덜 깨끗합니다.

어쩌면 나는 결코 충분히 어려운 문제에 부딪치지 않았지만 다른 사람들이 언급했듯이 명령 패턴과 디스패치를 ​​클래스 계층 구조로 인코딩하는 데 약간의 가치가 있다고 생각합니다. 문제의 핵심은 요청을 함수에 매핑하는 것입니다. 지도는 간단하고 명백하며 테스트하기 쉽습니다. 클래스 계층 구조에는 더 많은 코드와 디자인이 필요하며 해당 코드 사이의 연결이 증가하며 나중에 변경해야 할 추가 결정을 내릴 수 있습니다. 나는 명령 패턴이 전혀 적용되지 않는다고 생각합니다.


4

문제는 명령 디자인 패턴에 매우 적합 합니다. 따라서 기본적으로 기본 Command인터페이스가 있고 CommandImpl해당 인터페이스를 구현하는 여러 클래스가 있습니다. 인터페이스는 본질적으로 하나의 방법 만 있으면 doCommand(Args args)됩니다. Args클래스 의 인스턴스를 통해 인수를 전달할 수 있습니다 . 이 방법을 사용하면 복잡한 if / else 문 대신 다형성의 힘을 활용할 수 있습니다. 또한이 디자인은 쉽게 확장 할 수 있습니다.


3

switch 문 또는 OO 스타일 다형성을 사용해야하는지 묻는다면 Expression Problem을 참조하십시오 . 기본적으로 데이터에 대해 서로 다른 "케이스"가 있고 다른 "액션"(각 액션이 각 케이스마다 다르게 작동 함)을 지원하려는 경우에는 실제로 새로운 케이스와 새로운 액션을 모두 추가 할 수있는 시스템을 만들기가 어렵습니다. 앞으로.

switch 문 (또는 방문자 패턴)을 사용하는 경우 새 함수를 쉽게 추가 할 수 있지만 (단일 함수로 모든 항목을 쓰기 때문에) 새 케이스를 추가하기는 어렵습니다 (이전 함수로 돌아가서 이전 함수를 편집해야하기 때문).

반대로 OO 스타일 다형성을 사용하는 경우 새 사례를 쉽게 추가 할 수 있지만 (새 클래스 만들기) 인터페이스에 메서드를 추가하기가 어렵습니다 (다시 돌아가서 여러 클래스를 편집해야 함).

귀하의 경우 지원 해야하는 방법이 하나만 있지만 (요청 처리) 가능한 많은 경우 (각각 다른 명령)입니다. 새로운 사례를 쉽게 추가하는 것이 새로운 메소드를 추가하는 것보다 중요하므로 각 명령마다 별도의 클래스를 만드십시오.


그건 그렇고, 확장 가능한 것들의 관점에서 클래스 또는 함수를 사용하는지 여부에 큰 차이가 없습니다. switch 문과 비교할 때 중요한 것은 사물이 어떻게 "파견"되고 클래스와 함수가 동일한 방식으로 전달되는지입니다. 따라서 언어에서 더 편리한 것을 사용하십시오 (Go는 어휘 범위와 폐쇄가 있으므로 클래스와 함수의 구별은 실제로 매우 작습니다).

예를 들어 일반적으로 위임을 사용하여 상속에 의존하는 대신 오류 검사 부분을 수행 할 수 있습니다 (예 : Java 구문은 O 구문을 모르기 때문에 괜찮습니다)

function make_command(real_command){
    return function(x){
        if(check_arguments(x)){
            return real_command(x);
        }else{
            //handle error here
        }
    }
 }

 command1 = make_command(function(x){ 
     //do something
 })

 command2 = make_command(function(x){
     //do something else
 })

 command1(17);
 commnad2(42);

물론,이 예제는 모든 경우에 랩퍼 함수 또는 상위 클래스 점검 인수를 갖는 합리적인 방법이 있다고 가정합니다. 상황에 따라 명령 자체 내에서 check_arguments를 호출하는 것이 더 간단 할 수 있습니다 (각 명령은 다른 수의 인수, 다른 명령 유형 등으로 인해 다른 인수로 검사 기능을 호출해야 할 수 있기 때문에)

tl; dr : 모든 문제를 해결하는 가장 좋은 방법은 없습니다. "사물 만들기"관점에서 중요한 불변량을 적용하고 실수로부터 당신을 보호하는 방식으로 추상화를 만드는 데 집중하십시오. "미래 방지"관점에서 코드의 어떤 부분이 확장 될 가능성이 더 큰지 염두에 두십시오.


1

나는 AC # 프로그래머로서 go를 사용한 적이 없다. 아마 다음 줄로 넘어갈 것이다.

메인 함수를 실행하여 각각 작은 클래스 / 객체를 만들면 각 문자열 표현을 알아야합니다. 이것은 기능의 수가 증가함에 따라 원하는 것처럼 들리는 플러그 기능을 제공합니다. 실제로 필요한 경우가 아니라면 정적을 사용하지 않을 것입니다.

그런 다음 런타임에 이러한 클래스를 발견하여 다른 어셈블리 등에서로드되도록 변경하는 방법을 알고있는 팩토리를 갖게됩니다. 이는 동일한 프로젝트에서 모두 필요하지 않음을 의미합니다.

따라서 테스트를 위해 더 모듈화되고 코드를 훌륭하고 작게 만들어야하므로 나중에 유지 관리하기가 더 쉽습니다.


0

명령 / 기능을 어떻게 선택합니까?

올바른 기능을 선택하는 "영리한"방법이 있다면, 그렇게해야합니다. 코어 로직을 수정하지 않고도 새로운 지원을 추가 할 수 있습니다 (아마도 외부 라이브러리에서).

또한 개별 기능 테스트는 막대한 switch 문보다 분리하기 쉽습니다.

마지막으로 한 곳에서만 사용됩니다. 50에 도달하면 다른 기능의 다른 비트를 재사용 할 수 있다는 것을 알 수 있습니다.


명령은 고유 한 문자열입니다. 필요한 경우 정수에 매핑 할 수 있습니다.
beatgammit

0

Go가 어떻게 작동하는지 잘 모르겠지만 ActionScript에서 사용한 아키텍처는 책임 체인 역할을하는 이중 연결 목록을 갖는 것입니다. 각 링크에는 defineResponsibility 함수가 있습니다 (콜백으로 구현했지만 수행하려는 작업에 더 적합하면 각 링크를 별도로 작성할 수 있습니다). 링크에 책임이 있다고 판단되면 meetResponsibility (콜백)를 호출하고 체인을 종료합니다. 책임이 없다면 체인의 다음 링크로 요청을 전달합니다.

기존 체인의 링크 사이 (또는 끝에)에 새 링크를 추가하여 다른 사용 사례를 쉽게 추가하고 제거 할 수 있습니다.

이것은 함수 맵에 대한 아이디어와 비슷하지만 미묘하게 다릅니다. 좋은 점은 요청을 전달하고 다른 작업을 수행하지 않고도 그 일을한다는 것입니다. 단점은 값을 반환 해야하는 경우 제대로 작동하지 않는다는 것입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.