2014-03-27 14 views
4

나는 f #을 처음 사용합니다. 검색 결과 목록을 프런트 엔드에 반환하고 싶습니다. 그러나 각각 고유 한 속성 (예 : MovieResult.PosterIcon)을 가진 몇 가지 유형의 검색 결과 (예 : BlogResult, MovieResult 등)가있을 수 있습니다. UI는 이러한 유형을 인식하고 이에 따라 각 유형을 표시합니다. 핵심 검색 API는 이러한 유형을 인식하지 않으며 "제공자"가 반환해야하는 유형을 포함하여 구현해야하는 프레임 워크 만 제공합니다. 예를 들어 BlogProvider은 필요한 기능을 구현하며 해당 검색 결과 유형입니다. 응용 프로그램은 이러한 모든 공급자를 핵심 프레임 워크에 함께 등록하여 모든 결과가 항목의 단일 목록으로 순위가 지정된 순서로 반환되어야합니다. 이러한 공급자는 각각 응용 프로그램과 핵심 검색 API와 별도로 자체 어셈블리에 존재할 가능성이 큽니다.이 문제를 해결하는 적절한 방법은 상속입니까?

내 OO 두뇌는 다른 모든 결과 유형이 상속하는 SearchResult 클래스 또는 인터페이스를 원합니다. 이것을 처리하는 더 기능적인 방법이 있습니까?

업데이트 응용 프로그램은 ASP.NET MVC 응용 프로그램입니다. 각 결과는 부분보기로 표시됩니다. 새 검색 결과 유형이 추가되면 사이트 자체를 수정할 필요가 없습니다. 새 부분 뷰만 있습니다. 이것은 개방/폐쇄 원칙을 위반하지 않습니다. 전문적으로 공급자가 부분 뷰의 템플릿을 만들도록 할 수 있습니다. 그러나 의식 코드는 노력할만한 가치가 없습니다.

+0

@ user2864740 인터페이스는 구현이 전혀없는 영광스러운 추상 클래스에 불과합니다. 내 질문은 내 API를 구현하는 방법이 아니며 운영 계약입니다. 그것은 내 데이터 구조를 구현하는 방법에 관한 것이 었습니다. –

+0

@ user2864740 - 모든 OO 설계 원칙을 잘 알고 있습니다. 적절한 함수 프로그래밍 디자인 원리에 대해 묻고 있습니다. 이것이 나의 질문의 목적이다. 나는 클래스와 인터페이스의 차이점에 대해 신경 쓰지 않고있다. 내 요점은 상속을 사용하여 클래스 또는 인터페이스,이 상황에 대해 알고있는 유일한 옵션입니다. 표준 .NET 클래스 또는 인터페이스를 사용하는 f #에서는 상속 요구 사항으로 인해 구분 된 공용체 또는 레코드 유형을 공급자별로 사용할 수 없습니다. 만약 내가 틀렸다고 생각하지 않는다면 어떤 경우에 타입 유추도 제거합니다. –

+0

"help"에 대해 * serious *하다면 관련/제안 구조를 포함하십시오. 다시 말하지만,이 질문은 어떤 식 으로든 "순전히 기능적"과 관련이 없습니다. – user2864740

답변

2

내 제한된 상상력과 열악한 디자인 기술 일 수 있지만 위와 같은 요구 사항이 제시 될 때마다 나는 일종의 타협을해야했습니다. 다음은 OOP에서도 어려운 이유입니다.

UI는 이러한 유형을 인식하고 이에 따라 각 유형을 표시합니다.

이것은 문제가되는 부분입니다.

  • 취급 특별한 경우 모든 '하위 유형'지금까지 내가 말할 수있는 건,이 요구 사항을 해결하기 위해 두 가지 방법이있다. OOD를 사용하는 경우 클라이언트 (UI) 은 사용 가능한 모든 하위 유형에 대해에 대해 알고 있어야 함을 의미합니다. 애드혹 방식으로 다운 캐스트를 시도하거나 Visitor pattern을 활용할 수 있지만, UI 코드를 수정하지 않고 시스템에 새 '제공 업체'를 추가 할 수 없으므로 어느 경우이든 Open/Closed Principle을 위반합니다.
  • 모든 하위 유형에 공통 인터페이스을 구현하거나 공통 기본 클래스에서 상속 받도록합시다. 이 인터페이스 (또는 기본 클래스)에는 UI가 결과를 렌더링하기 위해 호출 할 수있는 하나 이상의 메소드가 있습니다. 이러한 방법은 렌더링 호출 할 수 있으며, 그것은 특정 기술이 될 것입니다 : 데스크톱 응용 프로그램에 대한
    • , 그것은처럼 보일 수 있습니다이 void Render(Canvas) (간체) - 즉,이 캔버스 개체의 일종에, 수신 그런 다음 렌더링을 요청합니다.
    • 웹 응용 프로그램의 경우 HTML 조각을 반환하는 함수 : string Render() (다시 간략하게 설명). 좀 더 정교한 계획을 마련 할 수 있지만

, 그들은 위의 두 대안의 조합 경향 것입니다.

각 대안은 상속에 의존하지 않고 F #에서 모델링 할 수 있습니다.

특례 대신 특별한 경우 각 '하위 유형을'치료

, 당신은 모든 다양한 경우의 차별 연합을 정의 할 수 있습니다

open System 

type BlogResult = { 
    Title : string 
    Summary : string } 

type MovieResult = { 
    Title : string 
    PosterIcon : Uri } 

type SearchResult = 
    | BlogResult of BlogResult 
    | MovieResult of MovieResult 

이가있다 특수 케이스에 대한 OOD 접근법과 동일한 단점 : 새로운 하위 유형을 도입하려는 경우 UI를 수정 (재 컴파일)해야합니다.

한편, F #에 구분 된 유니온은 쉽게 작업 할 수 있으며 컴파일 타임 검사 (Visitor 패턴을 사용할 수도 있음)까지 제공합니다.

공통 인터페이스를 사용하는 대신 공통 인터페이스

, 당신은 기능을 사용할 수 있습니다. 함수 또는 오히려 closures are equivalent to objects.

따라서 UI가 string Render() 메서드를 정의하는 인터페이스를 사용하게하는 대신 UI에이 서명이있는 함수 (예 : unit -> string)를 사용하게 할 수 있습니다.

시스템에 연결하려는 모든 '공급자'는 각 검색 결과에 대해이 서명이있는 함수를 반환하기 만하면됩니다. 그 기능은 폐쇄가 될 수 있습니다.

+0

열기/닫기 원칙 위반 문제에 대한 편집 내역보기 –

+0

후자의 경우 공급자가 렌더링 된 문자열 자체를 반환하지 않는 이유는 무엇입니까? –

+0

@DaxFohl 나는 당신의 질문을 이해하고 있는지 잘 모르겠다. 그게 내가 제안하는 함수 ('unit -> string')가하는 것이 아닙니까? –

2

면책 조항 :이 답변은 F #에는 적용되지 않습니다. Mark의 대답은 F #이 제공하는 최고의 답변입니다. 특히 차별화 된 노동 조합 접근법에는 C#이 직접적으로 동등한 것이 없습니다.

하지만 "기능적"에 대해 물어 보면 다른 일부 기능 언어가 더 나은 옵션을 가지고 있습니다. 스칼라 (Scala)와 하스켈 (Haskell)과 같은 다른 기능 언어는 typeclasses라는 기능을 제공합니다.이 기능은 본질적으로 인터페이스를 구현하는 확장 메서드를 만들 수 있고 그 후에 인터페이스에 볼트를 적용 할 수 있습니다.

그럼 어떻게 관련이 있습니까? F #의 경우, A) Discriminated Union에서 모든 사례를 사전 결정하거나 B) 데이터 라이브러리에 렌더링 기능을 넣어야합니다. 어느 쪽도 이상적이지 않습니다. typeclasses와

, 당신은 독립적으로 레코드 유형을 정의 할 수 있습니다 :

// Blog.fs 
type Blog {...} 
[<SomeProviderAttribute>]let getBlogs() = ... // returns Blog seq 

// Movie.fs 
type Movie = {...} 
[<SomeProviderAttribute>]let getMovies() = ... // returns Movie seq 

가 그럼 당신은 높은 수준에서 인터페이스에 볼트 수 : 메인 UI 지역에서 마지막으로

// UiBase.fs 
class Renderable where render :: 'T -> string 

// BlogUi.fs 
instance Renderable Blog where render blog = blog.header + blog.text 

// MovieUi.fs 
instance Renderable Movie where render movie = movie.title + movie.description 

(의사 코드는 다음과) 렌더링 된 문자열을 쉽게 수집 할 수 있습니다. 당신이 있었기 때문에, 상호 교환 높은 수준에서 클래스와 공급 업체를 블로그와 동영상

// MainUi.fs 
open Blog; open Movie; open UiBase; open BlogUi; open MovieUi 
// Just like you'd have to call "using" in C# to get the extention methods, 
// you have to open these to get the type classes 

let dataProviders = ... // Some assembly search and reflection here; 
        // say it returns [getBlogs; getMovies] 
        // (and note without the common typeclass, 
        // those two would be incompatible) 

let renderedStrings = 
    seq { for provider in dataProviders do 
      for renderable in provider() do 
      yield render renderable } 

그래서 기본적으로 당신이 (그렇지 않으면 전혀 관계가없는)을 사용할 수 있습니다 : 당신이 typeclasses을 정의하는 모듈을 열면 상호 교환 사용할 수 있습니다 중간 레벨에서 인터페이스를 "볼트로 고정"할 수 있습니다. 당신이 여기에서 직면하고있는 일반적인 문제에 대해 매우 시원하고 적절합니다.

...불행히도 F # 자체에는이 기능이 없습니다.

관련 문제