2014-10-29 4 views
1

После более чем 10 лет объектно-ориентированного программирования на C#, когда вы узнаете о F #, я борюсь с представлением о том, как проектировать приложения модульным способом, что позволяет добавлять функциональные возможности без изменения кода, который уже существует.F # Образцы дизайна

Как бы вы динамически добавляли случаи в дискриминационный союз?

Как вы работаете с разделяющим кодом в сборках, когда имеет значение заказ в файлах?

Как, например, шаблон стратегии вписывается в F #?

+3

Мне нравится вопрос, но он слишком широк. Пожалуйста, сосредоточьтесь на одном вопросе и покажите код, который у вас есть. Это даст нам лучший контекст. Общий вопрос о шаблонах F # должен быть в стороне, а не «мясом» сообщения. –

+0

Этот вопрос просто не подходит философии SOs - он широк и самоуверен - например, вы не можете «динамически» (если это означает вне его определения) степень DU, и это имеет какое-то отношение к [проблеме выражения] (https: //en.wikipedia.org/wiki/Expression_Problem), что приводит к очень длительному обсуждению – Carsten

+0

как разделить: я не собираю данные/функции по * домену *, но их в свой файл (s)/module (s) - у меня никогда не было проблемы с заказом - это просто заставляет вас больше думать о проблеме, когда вы сталкиваетесь с взаимным рекурсивным определением между файлами/пространствами имен/сборками (запах кода IMO) – Carsten

ответ

3

Вопрос пять частей, некоторые из которых еще не получили ответ.

... шаблоны проектирования в F # (в общем): вы можете реализовать известные формальные шаблоны проектирования в F # так же, как в C# или VB.NET (шаблоны проектирования GOF, шаблоны архитектуры предприятия, облака шаблоны проектирования и т. д.). В некоторых случаях реализация формального шаблона проектирования не требуется в F #, потому что язык имеет встроенные функции, которые делают шаблон устаревшим. Частым примером в этом отношении является шаблон посетителя. Однако замена формального шаблона проектирования на функциональную реализацию также возможна на C# или VB.NET (но менее элегантной). С другой стороны, функциональные языки имеют свои собственные «шаблоны», но обычно их не называют «шаблонами», поскольку они, как правило, являются просто алгоритмическими подходами, которые менее формализованы.

... позволяют добавить функциональность без изменения кода, который уже существует -> См. Ответ @ kvb.

... динамически добавлять случаи -> См. Ответ @ kvb.

... код деления в сборках когда заказ в файлах имеет значение: Это ограничение помогает написать код, путь которого прост для понимания. Взаимные зависимости, которые практически поощряются в C#/VB.NET, повысить сложность и «спагеттизировать» код. Однако, в некоторых случаях, взаимно зависимые конструкции являются законными. Вы можете определить их в F # четырьмя способами:

  1. Используйте тип класса. Члены одного и того же класса могут взаимно звонить друг другу независимо от порядка их заявлений.
  2. Используйте ключевое слово and, чтобы выполнять функции, значения или типы (в файле ), зависящих друг от друга.
  3. Использование расширений встроенного типа для определения частей типа до и после того, как определяются другими типов (в том же файл)
  4. Использования отделенного шаблон интерфейса разорвать взаимную зависимость.

... стратегический образец -> См. Также ответ цоколя. Вы можете сделать это в способе GOF, как в C# или VB.NET. Вы также можете сделать это функционально в 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. Это очень полезно, что множество возможных случаев дискриминированных союзов известно во время компиляции, так что компилятор может проверить, когда ваш код не обрабатывает возможный случай.

  2. Существует большой ресурс, который обсуждают дизайн F # программы Я думаю, что это может ответить на ваш вопрос: http://fsharpforfunandprofit.com/posts/recipe-part3/

  3. Хороший ответ (ов) можно найти здесь: Strategy pattern in F#

+0

Взгляните на [эта ссылка от programers.stackexchange.com] (http://programmers.stackexchange.com/questions/209357/from-a-high-level-programming-perspective-where-does-the-different- парадигма-б). –

2

Так как функции являются манипулируемыми и сложными в 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, которая добавит смещение смещения к его аргументу. Эта функция, в свою очередь, частично применяется к pathPointMutator, которая возвращает функцию PathOp->PathOp, которая частично применяется к pathMutator, возвращающему функцию Seq<PathOp>->Seq<PathOp>.

По существу, я создал способ применения стратегии к каждой операции пути для создания новых действий пути.

Вместо того чтобы использовать интерфейс для определения стратегии, вместо этого я использую строго типизированную функцию для определения стратегии. Концепция та же, но конкретная реализация отличается.

Если вы хотите, чтобы взаимодействовать с другими языками .NET вы можете сделать это с помощью интерфейсов, если вы действительно хотите:

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 

Это, конечно, слишком много работы, чтобы сделать расчеты, но ваше внимание должен быть на слепом использовании ICalculator, не зная его деталей реализации.

Теперь, если быть точным в определении стратегии, Responder является более точным в том, что фактическая стратегия выбрана позднее, тогда как в примере PathOp это делается априори. Это одна из вещей в шаблонах GoF, которые могут усугубляться, если вы пытаетесь быть точными - многие из шаблонов перекрываются, и ваша фактическая реализация может не иметь прочной таксономии.

+0

Я более или менее понимаю ваш подход, но тогда ваш код F # не был бы расширяемым, не так ли? Вы не можете добавлять новые копии в DU без изменения самого DU. – vtortola

+0

Это не имеет никакого отношения к добавлению в DU. Как утверждали другие, DU являются статическими (и, следовательно, безопасными для типа компиляции). – plinth

+0

Добавление случаев в DU было одним из вопросов, так как я был обеспокоен расширяемостью. – vtortola

1

Если вы знаете, что вам необходимо расширить набор вариантов гибким способом, тогда дискриминационный союз, вероятно, не подходит для моделирования вашей проблемы. Вместо этого, дискриминационные союзы наиболее полезны, когда впереди все возможное множество вариантов выбора (например, вы можете моделировать список как пустой или имеющий первый элемент плюс остаток, и эта модель никогда не понадобится изменять).

Если вы хотите расширить свой дизайн непредвиденными способами, то использование иерархий типа C# (или, по крайней мере, интерфейсов) часто является очень разумным подходом даже в F #. В некоторых случаях более простые подходы, такие как записи функций, также могут работать очень хорошо (и на самом деле существует довольно глубокое соединение между функциями и объектами - см., Например, http://c2.com/cgi/wiki?ClosuresAndObjectsAreEquivalent и http://people.csail.mit.edu/gregs/ll1-discuss-archive-html/msg03277.html, к которым оно относится).

Наконец, я бы не стал защищать его, но там является способом использовать дискриминационные союзы в довольно сложной схеме для решения «проблемы выражения»; см. сообщение Vesa Karvonen здесь: http://lambda-the-ultimate.org/node/2232#comment-31278 (хотя, если вы новичок в ML-иш-языках, это может быть непонятно).

Смежные вопросы