2014-10-29 3 views
1

10 년 이상 C#으로 객체 지향 프로그래밍을 한 후 F #에 대해 배울 때 모듈 방식으로 응용 프로그램을 디자인하는 방법을 상상하기 어려워서 이미 수정 된 코드없이 기능을 추가 할 수 있습니다.F # 디자인 패턴

어떻게 차별화 된 공용체에 사례를 동적으로 추가 하시겠습니까?

파일의 순서가 중요한 경우 어셈블리에서 코드를 어떻게 분할합니까?

예를 들어 전략 패턴이 F #에 맞습니까?

+3

등의 추가 기능 기능을 가지고 있습니다. 한 가지 질문에 초점을 맞추고 가지고있는 코드를 보여주십시오. 그것은 우리에게 더 나은 맥락을 줄 것입니다. 일반적인 F # 패턴 질문은 게시물의 "고기"보다는 제쳐두어 야합니다. –

+0

이 질문은 단지 SO의 철학에 맞지 않습니다 - 광범위하고 독단적입니다 - 예를 들어, 당신은 "동적으로"(정의의 바깥 쪽을 의미하는 경우) 범위 DU가 될 수없고 이것은 [표현 문제] (https : //en.wikipedia.org/wiki/Expression_Problem) - 매우 긴 토론으로 이어지는 – Carsten

+0

나누기 : 나는 - 도메인 *에 의해 데이터/기능을 수집하지만 그 파일을 자신의 파일로 s)/module (s) - 주문과 관련된 문제가 없었습니다. 파일/네임 스페이스/어셈블리 (코드 냄새 IMO)에 대한 상호 재귀 적 정의에 직면 할 때 문제에 대해 더 많이 생각하게 만듭니다. – Carsten

답변

3

질문에 다섯 부분이 있는데, 그 중 일부는 아직 답변되지 않았습니다. 당신은 당신이 C# 또는 VB.NET (GOF 디자인 패턴, 엔터프라이즈 아키텍처 패턴, 구름에서와 마찬가지로 F 번호에서 잘 알려진 공식 디자인 패턴을 구현할 수 : F 번호 (일반)에서

... 디자인 패턴 디자인 패턴 등). 어떤 경우에는 F #에서 공식적인 디자인 패턴을 구현하는 것이 필요하지 않습니다. 언어에 패턴을 쓸모없는 내장 기능이 있기 때문입니다. 이 점에서 자주 인용되는 예는 방문자 패턴입니다. 그러나 공식적인 디자인 패턴을 함수 구현으로 바꾸는 것도 C#이나 VB.NET에서 가능합니다 (그러나 덜 우아함). 반면에, 함수형 언어는 자신의 "패턴"을 가지고 있지만 덜 공식적인 단지 알고리즘 접근, 경향으로 그들은 일반적으로 "패턴"이라고 아닙니다.

... 이미있는 코드를 수정하지 않고 기능을 추가 할 수 있습니다. -> @ kvb의 대답을 참조하십시오.

... 동적으로 사례 추가 -> @ kvb의 답변을 참조하십시오.

... 어셈블리의 코드를 구분 파일의 순서가 중요한 경우 :이 제한은 경로가 이해하기 쉬운 코드를 작성하는 데 도움이됩니다. C#/VB에서 실질적으로 권장되는 상호 의존성.NET에서 복잡성을 높이고 코드를 "스파게티 화"하십시오. 그러나 어떤 경우에는 상호 의존성과 같은 구성이 합법적입니다. F #에서 네 가지 방법으로 정의 할 수 있습니다.

  1. 클래스 유형을 사용하십시오. 동일한 클래스의 멤버는 선언 순서에 관계없이 상호 호출 할 수 있습니다.
  2. and 키워드를 사용하여 함수, 값 또는 형식 (동일한 파일 )을 서로 종속되게 만듭니다.
  3. (같은 파일에서) 유형을 정의하기 위해 내장 유형 확장을 사용하십시오.
  4. 상호 의존성을 제거하려면 분리 된 인터페이스 패턴을 사용하십시오.

... 전략 패턴 -> @ plinth의 대답도 참조하십시오. 당신은 C# 또는 VB.NET 에서처럼 GOF 방식으로 할 수 있습니다. F #, C# 또는 VB.NET (모든 함수 언어)에서 기능적으로 수행 할 수도 있습니다. F #을하지만, 표현/더 간결하고 내가 질문을 좋아하지만 너무 광범위 등의 기능을 조합, 부분적인 기능 응용 프로그램 등 :

let stratAdd x y = x + y 
let stratMul x y = x * y 

let partialStratAdd = stratAdd 10 
let partialStratMul = stratMul 20 

let chosenStrat = 
    if Random().Next(1, 100) < 50 then partialStratAdd 
    else partialStratMul 

chosenStrat 7 // Gets either 17 or 140 
+0

그 점에 대해 감사드립니다. 나는 앞으로 긴 길을 가고있는 것 같습니다. – vtortola

1

문제는 일반의 종류하지만보다 구체적인 질문에 대한 대답을 시도 할 수 있습니다 :

차별 노동 조합의 가지 경우의 설정은 컴파일 시간에 알려진 것이 많은 도움이 될 것입니다
  1. 때문에, 컴파일러는 코드가 가능한 경우를 처리하지 않을 때를 확인할 수 있습니다. http://fsharpforfunandprofit.com/posts/recipe-part3/

  2. 좋은 대답 (들) 여기에서 볼 수 있습니다 : Strategy pattern in F#

+0

[이 링크는 programmers.stackexchange.com]에서보십시오 (http://programmers.stackexchange.com/questions/209357/from-a-high-level-programming-perspective-where-does-the-different- 패러다임 -b). –

2

이후

  • 나는 그것을 할 수 생각 F # 프로그램의 디자인을 논의 훌륭한 자원이있다 귀하의 질문에 대한 대답 기능은 조작 가능하고 F #에서 구성 가능하며, 전략 패턴은 유비쿼터스입니다.

    type Point = { X:float; Y:float } 
    type PathOp = 
    | Close 
    | MoveTo of Point 
    | LineTo of Point 
    | CurveTo of Point * Point * Point 
    

    및 함수 형식 :

    type PointMutator = Point->Point 
    type PathOpMutator = PathOp->PathOp 
    

    NB : 예

    내가 점 및 경로 요소 타입 정의 있다고 가정이 문맥에서 테이터 주어진 함수 객체는 내용이 동일한 방식으로 변경된 객체를 반환합니다 (어쩌면). 이 같은 경로 시퀀스의 요소를 변경하는 것을 목적으로 쓸 수

    각의 변이를 만드는 경로 동작의 새로운 시퀀스를 생성
    let pathMutator (mutator:PathOpMutator) path = 
        path |> Seq.map(mutator) 
    

    . 이제

    ,이 작업을 수행 할 수 있습니다 그래서

    let pathPointMutator (mutator:PointMutator) op = 
    match op with 
    | Close -> Close 
    | MoveTo(pt) -> MoveTo(mutator(pt)) 
    | LineTo(pt) -> LineTo(mutator(pt)) 
    | CurveTo(cp1, cp2, dp) -> CurveTo(mutator(cp1), mutator(cp2), mutator(cp3)) 
    

    을 내가, 내가 오프셋 전략 적용 경로의 각 요소를 방문 할 경로의 전체 내용을 상쇄하기 위해 코드를 작성하기를 원한다면 :

    let pointOffseter offset pt = 
        { X = offset.X + pt.X; Y = offset.Y + pt.Y } 
    
    let offsetPath offset = pathMutator (pathPointMutator (pointOffsetter offset)) 
    

    여기서는 부분 함수 응용 프로그램을 많이 사용하고 있습니다. pointOffsetter에 오프셋 만 전달하면 Point->Point의 함수가 반환되어 인수에 바운드 오프셋이 추가됩니다. 이 함수는 부분적으로 의 함수를 반환하는 pathMutator에 부분적으로 적용되는 PathOp->PathOp의 함수를 반환하는 pathPointMutator에 부분적으로 적용됩니다.

    본질적으로, 내가 한 것은 새로운 경로 작업을 생성하기 위해 각 경로 작업에 전략을 적용하는 방법으로 만들어졌습니다.

    대신 전략을 정의하기 위해 인터페이스를 사용하는 대신 전략을 정의하기 위해 강력한 형식의 함수를 사용합니다. 개념은 동일하지만 구체적인 구현은 다릅니다. 이것은 물론, 너무 많은 일을 그냥 계산을하는 것입니다

    type ICalculate = 
        abstract member Calculate : float->float->float 
    
    type Adder = 
        interface ICalculate with 
         member this.Calculate x y = x + y 
    
    type Subber = 
        interface ICalculate with 
         member this.Calculate x y = x - y 
    
    type Responder = 
        member private this.GetCalculator(op) = 
        match op with 
        | '+' = new Adder() 
        | '-' = new Subber() 
        | _ -> ivalidArg "op" "op not defined" 
        member this.Respond op x y = 
         let calc = GetCalculator(op) 
         calc x y 
    

    하지만 초점 : 당신은 당신이 인터페이스를 사용하여이 작업을 수행 할 수있는 다른 .NET 언어와 상호 운용하려면

    당신이 정말로 원하는 경우 구현 세부 사항을 모른 채 ICalculator를 사용하지 않아야합니다.

    이제 전략 정의에서보다 정확하게 말하자면 Responder은 실제 전략이 늦게 선택된다는 점에서 더 정확합니다. 반면 PathOp 예에서는 선험적으로 수행됩니다. 이 패턴은 GoF 패턴에서 중요한 요소 중 하나입니다. 패턴의 상당 부분이 겹치고 실제 구현에는 견고한 분류가 없을 수도 있습니다.

  • +0

    나는 당신의 접근 방식을 어느 정도 이해하지만, F # 코드는 확장 할 수 없을 것입니다. 맞습니까? DU 자체를 변경하지 않고도 DU에 신제품을 추가 할 수있는 방법은 없습니다. – vtortola

    +0

    이것은 하위 사용자에게 추가하는 것과 아무런 관련이 없습니다. 다른 사람들이 말했듯이, DU는 정적 (따라서 컴파일 타임 타입 안전)입니다. – plinth

    +0

    DU에 사례를 추가하는 것은 확장성에 우려를 나타 내기 때문에 질문 중 하나였습니다. – vtortola

    1

    유연한 방식으로 선택 사항을 확장해야하는 경우 차별화 된 조합은 문제를 모델링하는 올바른 방법이 아닐 수 있습니다. 대신 차별화 된 노조는 가능한 선택의 세계가 알려지면 가장 유용합니다 (예 : 목록을 비어 있거나 첫 번째 항목에 나머지를 더한 것으로 모델링 할 수 있으며 모델을 변경할 필요가없는 경우).

    예기치 않은 방식으로 디자인을 확장하려면 F # 에서조차도 C#과 유사한 형식 계층 구조 (또는 최소한 인터페이스)를 사용하는 것이 종종 매우 합리적인 방법입니다. 어떤 경우에는, 기능의 기록과 같은 간단한 방법도 매우 잘 작동 할 수 있습니다 (그리고 기능과 객체 사이의 상당히 깊은 연결이 실제로있다 -이 참조하는, 예를 들어, http://c2.com/cgi/wiki?ClosuresAndObjectsAreEquivalenthttp://people.csail.mit.edu/gregs/ll1-discuss-archive-html/msg03277.html 참조).

    마지막으로, 나는 그것을지지하지 않을 것이지만, "표현 문제"를 해결하기 위해 꽤 복잡한 계획으로 차별화 된 노동 조합을 사용하는 방법입니다. 여기 Vesa Karvonen의 게시물을 참조하십시오 : http://lambda-the-ultimate.org/node/2232#comment-31278 (ML-ish 언어가 처음이라면 이해할 수 없겠지만).